GCC Code Coverage Report


Directory: ./
File: V3Width.cpp
Date: 2025-04-17 21:42:37
Exec Total Coverage
Lines: 487 3725 13.1%
Functions: 84 395 21.3%
Branches: 397 7183 5.5%

Line Branch Exec Source
1 // -*- mode: C++; c-file-style: "cc-mode" -*-
2 //*************************************************************************
3 // DESCRIPTION: Verilator: Expression width calculations
4 //
5 // Code available from: https://verilator.org
6 //
7 //*************************************************************************
8 //
9 // Copyright 2003-2025 by Wilson Snyder. This program is free software; you
10 // can redistribute it and/or modify it under the terms of either the GNU
11 // Lesser General Public License Version 3 or the Perl Artistic License
12 // Version 2.0.
13 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 //
15 //*************************************************************************
16 // V3Width's Transformations:
17 // Top down traversal:
18 // Determine width of sub-expressions
19 // width() = # bits upper expression wants, 0 for anything-goes
20 // widthUnsized() = # bits for unsized constant, or 0 if it's sized
21 // widthMin() = Alternative acceptable width for linting, or width() if sized
22 // Determine this subop's width, can be either:
23 // Fixed width X
24 // Unsized, min width X ('d5 is unsized, min 3 bits.)
25 // Pass up:
26 // width() = # bits this expression generates
27 // widthSized() = true if all constants sized, else false
28 // Compute size of this expression
29 // Lint warn about mismatches
30 // If expr size != subop fixed, bad
31 // If expr size < subop unsized minimum, bad
32 // If expr size != subop, edit netlist
33 // For == and similar ops, if multibit underneath, add a REDOR
34 // If subop larger, add a EXTRACT
35 // If subop smaller, add a EXTEND
36 // Pass size to sub-expressions if required (+/-* etc)
37 // FINAL = true.
38 // Subexpressions lint and extend as needed
39 //
40 //*************************************************************************
41 // Signedness depends on:
42 // Decimal numbers are signed
43 // Based numbers are unsigned unless 's' prefix
44 // Comparison results are unsigned
45 // Bit&Part selects are unsigned, even if whole
46 // Concatenates are unsigned
47 // Ignore signedness of self-determined:
48 // shift rhs, ** rhs, x?: lhs, concat and replicate members
49 // Else, if any operand unsigned, output unsigned
50 //
51 // Real number rules:
52 // Real numbers are real (duh)
53 // Reals convert to integers by rounding
54 // Reals init to 0.0
55 // Logicals convert compared to zero
56 // If any operand is real, result is real
57 //*************************************************************************
58 // V3Width is the only visitor that uses vup. We could switch to using userp,
59 // though note some iterators operate on next() and so would need to pass the
60 // same value on each nextp().
61 //*************************************************************************
62 // See notes in internal.txt about misuse of iterateAndNext and use of
63 // iterateSubtreeReturnEdits.
64 //*************************************************************************
65
66 #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
67
68 #include "V3Width.h"
69
70 #include "V3Ast.h"
71 #include "V3Begin.h"
72 #include "V3Const.h"
73 #include "V3Error.h"
74 #include "V3Global.h"
75 #include "V3LinkLValue.h"
76 #include "V3MemberMap.h"
77 #include "V3Number.h"
78 #include "V3Randomize.h"
79 #include "V3String.h"
80 #include "V3Task.h"
81 #include "V3WidthCommit.h"
82
83 // More code; this file was getting too large; see actions there
84 #define VERILATOR_V3WIDTH_CPP_
85 #include "V3WidthRemove.h"
86
87
10/19
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 59850 times.
✓ Branch 2 taken 80 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 240 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 80 times.
✓ Branch 8 taken 160 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 80 times.
✓ Branch 11 taken 160 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 80 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 80 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
121300 VL_DEFINE_DEBUG_FUNCTIONS;
88
89 //######################################################################
90
91 enum Stage : uint8_t {
92 PRELIM = 1,
93 FINAL = 2,
94 BOTH = 3
95 }; // Numbers are a bitmask <0>=prelim, <1>=final
96 std::ostream& operator<<(std::ostream& str, const Stage& rhs) {
97 return str << ("-PFB"[static_cast<int>(rhs)]);
98 }
99
100 enum Determ : uint8_t {
101 SELF, // Self-determined
102 CONTEXT_DET, // Context-determined
103 ASSIGN // Assignment-like where sign comes from RHS only
104 };
105 std::ostream& operator<<(std::ostream& str, const Determ& rhs) {
106 static const char* const s_det[] = {"SELF", "CNTX", "ASSN"};
107 return str << s_det[rhs];
108 }
109
110 #define v3widthWarn(lhs, rhs, msg) \
111 v3errorEnd( \
112 v3errorBuildMessage(V3Error::v3errorPrep((lhs) < (rhs) ? V3ErrorCode::WIDTHTRUNC \
113 : (lhs) > (rhs) ? V3ErrorCode::WIDTHEXPAND \
114 : V3ErrorCode::WIDTH), \
115 msg))
116
117 //######################################################################
118 // Width state, as a visitor of each AstNode
119
120 class WidthVP final {
121 // Parameters to pass down hierarchy with visit functions.
122 AstNodeDType* const m_dtypep; // Parent's data type to resolve to
123 const Stage m_stage; // If true, report errors
124 public:
125 WidthVP(AstNodeDType* dtypep, Stage stage)
126 59912 : m_dtypep{dtypep}
127
3/34
✓ Branch 1 taken 43193 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 3578 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5342 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
59542 , m_stage{stage} {
128 // Prelim doesn't look at assignments, so shouldn't need a dtype,
129 // however AstPattern uses them
130 }
131 156254 WidthVP(Determ determ, Stage stage)
132 156254 : m_dtypep{nullptr}
133 156254 , m_stage{stage} {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156254 times.
156254 if (determ != SELF && stage != PRELIM)
135 v3fatalSrc("Context-determined width request only allowed as prelim step");
136 156254 }
137 WidthVP* p() { return this; }
138 bool selfDtm() const { return m_dtypep == nullptr; }
139 AstNodeDType* dtypep() const {
140 // Detect where overrideDType is probably the intended call
141 UASSERT(m_dtypep, "Width dtype request on self-determined or preliminary VUP");
142 return m_dtypep;
143 }
144 21785 AstNodeDType* dtypeNullp() const { return m_dtypep; }
145 AstNodeDType* dtypeNullSkipRefp() const {
146 AstNodeDType* dtp = dtypeNullp();
147 if (dtp) dtp = dtp->skipRefp();
148 return dtp;
149 }
150 3313 AstNodeDType* dtypeOverridep(AstNodeDType* defaultp) const {
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3313 times.
3313 UASSERT(m_stage != PRELIM, "Parent dtype should be a final-stage action");
152
2/2
✓ Branch 0 taken 3272 times.
✓ Branch 1 taken 41 times.
3313 return m_dtypep ? m_dtypep : defaultp;
153 }
154 int width() const {
155 UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP");
156 return m_dtypep->width();
157 }
158 int widthMin() const {
159 UASSERT(m_dtypep, "Width request on self-determined or preliminary VUP");
160 return m_dtypep->widthMin();
161 }
162 75174 bool prelim() const { return m_stage & PRELIM; }
163 29088 bool final() const { return m_stage & FINAL; }
164 void dump(std::ostream& str) const {
165 if (!m_dtypep) {
166 str << " VUP(s=" << m_stage << ",self)";
167 } else {
168 str << " VUP(s=" << m_stage << ",dt=" << cvtToHex(dtypep());
169 dtypep()->dumpSmall(str);
170 str << ")";
171 }
172 }
173 };
174 std::ostream& operator<<(std::ostream& str, const WidthVP* vup) {
175 if (vup) vup->dump(str);
176 return str;
177 }
178
179 //######################################################################
180
181 class WidthClearVisitor final {
182 // Rather than a VNVisitor, can just quickly touch every node
183 73194 void clearWidthRecurse(AstNode* nodep) {
184
2/2
✓ Branch 0 taken 81795 times.
✓ Branch 1 taken 73194 times.
154989 for (; nodep; nodep = nodep->nextp()) {
185 nodep->didWidth(false);
186
2/2
✓ Branch 0 taken 38087 times.
✓ Branch 1 taken 43708 times.
81795 if (nodep->op1p()) clearWidthRecurse(nodep->op1p());
187
2/2
✓ Branch 0 taken 27451 times.
✓ Branch 1 taken 54344 times.
81795 if (nodep->op2p()) clearWidthRecurse(nodep->op2p());
188
2/2
✓ Branch 0 taken 4008 times.
✓ Branch 1 taken 77787 times.
81795 if (nodep->op3p()) clearWidthRecurse(nodep->op3p());
189
2/2
✓ Branch 0 taken 3568 times.
✓ Branch 1 taken 78227 times.
81795 if (nodep->op4p()) clearWidthRecurse(nodep->op4p());
190 }
191 73194 }
192
193 public:
194 // CONSTRUCTORS
195 80 explicit WidthClearVisitor(AstNetlist* nodep) { clearWidthRecurse(nodep); }
196 80 virtual ~WidthClearVisitor() = default;
197 };
198
199 //######################################################################
200
201 #define accept in_WidthVisitor_use_AstNode_iterate_instead_of_AstNode_accept
202
203 //######################################################################
204
205 class WidthVisitor final : public VNVisitor {
206 // TYPES
207 using TableMap = std::map<std::pair<const AstNodeDType*, VAttrType>, AstVar*>;
208 using PatVecMap = std::map<int, AstPatMember*>;
209 using DTypeMap = std::map<const std::string, AstPatMember*>;
210
211 // STATE
212 VMemberMap m_memberMap; // Member names cached for fast lookup
213 V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
214 WidthVP* m_vup = nullptr; // Current node state
215 const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
216 const AstEnumItem* m_enumItemp = nullptr; // Current enum item
217 const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
218 const AstConstraint* m_constraintp = nullptr; // Current constraint
219 const AstNodeProcedure* m_procedurep = nullptr; // Current final/always
220 const AstWith* m_withp = nullptr; // Current 'with' statement
221 const AstFunc* m_funcp = nullptr; // Current function
222 const AstAttrOf* m_attrp = nullptr; // Current attribute
223 const AstNodeExpr* m_randomizeFromp = nullptr; // Current randomize method call fromp
224 const bool m_paramsOnly; // Computing parameter value; limit operation
225 const bool m_doGenerate; // Do errors later inside generate statement
226 int m_dtTables = 0; // Number of created data type tables
227 TableMap m_tableMap; // Created tables so can remove duplicates
228 std::map<const AstNodeDType*, AstQueueDType*>
229 m_queueDTypeIndexed; // Queues with given index type
230
231 static constexpr int ENUM_LOOKUP_BITS = 16; // Maximum # bits to make enum lookup table
232
233 // ENUMS
234 enum ExtendRule : uint8_t {
235 EXTEND_EXP, // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y"
236 EXTEND_ZERO, // Extend with zeros. e.g. node=y in EQ(x,y), "x == y"
237 EXTEND_LHS, // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y"
238 EXTEND_OFF // No extension
239 };
240
241 // VISITORS
242 // Naming: width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing}
243 // Where type:
244 // _O1=boolean (width 1 unsigned)
245 // _Ou=unsigned
246 // _Os=signed
247 // _Ous=unsigned or signed
248 // _Or=real
249 // _Ox=anything
250
251 // Widths: 1 bit out, lhs 1 bit; Real: converts via compare with 0
252 461 void visit(AstLogNot* nodep) override { visit_log_not(nodep); }
253 // Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0
254 112 void visit(AstLogAnd* nodep) override { visit_log_and_or(nodep); }
255 84 void visit(AstLogOr* nodep) override { visit_log_and_or(nodep); }
256 void visit(AstLogEq* nodep) override {
257 // Conversion from real not in IEEE, but a fallout
258 visit_log_and_or(nodep);
259 }
260 void visit(AstLogIf* nodep) override {
261 // Conversion from real not in IEEE, but a fallout
262 visit_log_and_or(nodep);
263 }
264
265 // Widths: 1 bit out, Any width lhs
266 106 void visit(AstRedAnd* nodep) override { visit_red_and_or(nodep); }
267 292 void visit(AstRedOr* nodep) override { visit_red_and_or(nodep); }
268 203 void visit(AstRedXor* nodep) override { visit_red_and_or(nodep); }
269 void visit(AstOneHot* nodep) override { visit_red_and_or(nodep); }
270 void visit(AstOneHot0* nodep) override { visit_red_and_or(nodep); }
271 void visit(AstIsUnknown* nodep) override {
272 visit_red_unknown(nodep); // Allow real
273 }
274
275 // These have different node types, as they operate differently
276 // Must add to case statement below,
277 // Widths: 1 bit out, lhs width == rhs width. real if lhs|rhs real
278 122 void visit(AstEq* nodep) override { visit_cmp_eq_gt(nodep, true); }
279 108 void visit(AstNeq* nodep) override { visit_cmp_eq_gt(nodep, true); }
280 96 void visit(AstGt* nodep) override { visit_cmp_eq_gt(nodep, true); }
281 122 void visit(AstGte* nodep) override { visit_cmp_eq_gt(nodep, true); }
282 144 void visit(AstLt* nodep) override { visit_cmp_eq_gt(nodep, true); }
283 190 void visit(AstLte* nodep) override { visit_cmp_eq_gt(nodep, true); }
284 void visit(AstGtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
285 void visit(AstGteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
286 void visit(AstLtS* nodep) override { visit_cmp_eq_gt(nodep, true); }
287 void visit(AstLteS* nodep) override { visit_cmp_eq_gt(nodep, true); }
288 132 void visit(AstEqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
289 106 void visit(AstNeqCase* nodep) override { visit_cmp_eq_gt(nodep, true); }
290 // ... These comparisons don't allow reals
291 void visit(AstEqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
292 void visit(AstNeqWild* nodep) override { visit_cmp_eq_gt(nodep, false); }
293 // ... Real compares
294 void visit(AstEqD* nodep) override { visit_cmp_real(nodep); }
295 void visit(AstNeqD* nodep) override { visit_cmp_real(nodep); }
296 void visit(AstLtD* nodep) override { visit_cmp_real(nodep); }
297 void visit(AstLteD* nodep) override { visit_cmp_real(nodep); }
298 void visit(AstGtD* nodep) override { visit_cmp_real(nodep); }
299 void visit(AstGteD* nodep) override { visit_cmp_real(nodep); }
300 // ... String compares
301 void visit(AstEqN* nodep) override { visit_cmp_string(nodep); }
302 void visit(AstNeqN* nodep) override { visit_cmp_string(nodep); }
303 void visit(AstLtN* nodep) override { visit_cmp_string(nodep); }
304 void visit(AstLteN* nodep) override { visit_cmp_string(nodep); }
305 void visit(AstGtN* nodep) override { visit_cmp_string(nodep); }
306 void visit(AstGteN* nodep) override { visit_cmp_string(nodep); }
307 // ... Data type compares
308 void visit(AstEqT* nodep) override { visit_cmp_type(nodep); }
309 void visit(AstNeqT* nodep) override { visit_cmp_type(nodep); }
310
311 // Widths: out width = lhs width = rhs width
312 // Signed: Output signed iff LHS & RHS signed.
313 // Real: Not allowed
314 1140 void visit(AstAnd* nodep) override { visit_boolexpr_and_or(nodep); }
315 1274 void visit(AstOr* nodep) override { visit_boolexpr_and_or(nodep); }
316 536 void visit(AstXor* nodep) override { visit_boolexpr_and_or(nodep); }
317 void visit(AstBufIf1* nodep) override {
318 visit_boolexpr_and_or(nodep);
319 } // Signed behavior changing in 3.814
320 // Width: Max(Lhs,Rhs) sort of.
321 // Real: If either side real
322 // Signed: If both sides real
323 78 void visit(AstAdd* nodep) override { visit_add_sub_replace(nodep, true); }
324 82 void visit(AstSub* nodep) override { visit_add_sub_replace(nodep, true); }
325 256 void visit(AstDiv* nodep) override { visit_add_sub_replace(nodep, true); }
326 240 void visit(AstMul* nodep) override { visit_add_sub_replace(nodep, true); }
327 // These can't promote to real
328 256 void visit(AstModDiv* nodep) override { visit_add_sub_replace(nodep, false); }
329 void visit(AstModDivS* nodep) override { visit_add_sub_replace(nodep, false); }
330 void visit(AstMulS* nodep) override { visit_add_sub_replace(nodep, false); }
331 void visit(AstDivS* nodep) override { visit_add_sub_replace(nodep, false); }
332 // Widths: out width = lhs width, but upper matters
333 // Signed: Output signed iff LHS signed; unary operator
334 // Unary promote to real
335 104 void visit(AstNegate* nodep) override { visit_negate_not(nodep, true); }
336 // Unary never real
337 1592 void visit(AstNot* nodep) override { visit_negate_not(nodep, false); }
338
339 // Real: inputs and output real
340 void visit(AstAddD* nodep) override { visit_real_add_sub(nodep); }
341 void visit(AstSubD* nodep) override { visit_real_add_sub(nodep); }
342 void visit(AstDivD* nodep) override { visit_real_add_sub(nodep); }
343 void visit(AstMulD* nodep) override { visit_real_add_sub(nodep); }
344 void visit(AstPowD* nodep) override { visit_real_add_sub(nodep); }
345 void visit(AstNodeSystemBiopD* nodep) override { visit_real_add_sub(nodep); }
346 // Real: Output real
347 void visit(AstNegateD* nodep) override { visit_real_neg_ceil(nodep); }
348 void visit(AstNodeSystemUniopD* nodep) override { visit_real_neg_ceil(nodep); }
349
350 // Widths: out signed/unsigned width = lhs width, input un|signed
351 void visit(AstSigned* nodep) override { visit_signed_unsigned(nodep, VSigning::SIGNED); }
352 void visit(AstUnsigned* nodep) override { visit_signed_unsigned(nodep, VSigning::UNSIGNED); }
353
354 // Widths: Output width from lhs, rhs<33 bits
355 // Signed: If lhs signed
356 288 void visit(AstShiftL* nodep) override { visit_shift(nodep); }
357 252 void visit(AstShiftR* nodep) override { visit_shift(nodep); }
358 // ShiftRS converts to ShiftR, but not vice-versa
359 158 void visit(AstShiftRS* nodep) override { visit_shift(nodep); }
360
361 //========
362 // Widths: Output real, input integer signed
363 void visit(AstBitsToRealD* nodep) override { visit_Or_Lu64(nodep); }
364
365 // Widths: Output integer signed, input real
366 void visit(AstRToIS* nodep) override { visit_Os32_Lr(nodep); }
367 void visit(AstRToIRoundS* nodep) override {
368 // Only created here, size comes from upper expression
369 if (m_vup->prelim()) { // First stage evaluation
370 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
371 }
372 UASSERT_OBJ(nodep->dtypep()->widthSized(), nodep, "RToIRoundS should be presized");
373 }
374
375 // Widths: Output integer unsigned, input real
376 void visit(AstRealToBits* nodep) override { visit_Ou64_Lr(nodep); }
377
378 // Output integer, input string
379 void visit(AstLenN* nodep) override {
380 // Widths: 32 bit out
381 UASSERT_OBJ(nodep->lhsp(), nodep, "For unary ops only!");
382 if (m_vup->prelim()) {
383 // See similar handling in visit_cmp_eq_gt where created
384 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
385 nodep->dtypeSetSigned32();
386 }
387 }
388 void visit(AstPutcN* nodep) override {
389 // CALLER: str.putc()
390 UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
391 if (m_vup && m_vup->prelim()) {
392 // See similar handling in visit_cmp_eq_gt where created
393 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
394 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
395 iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
396 nodep->dtypeSetString(); // AstPutcN returns the new string to be assigned by
397 // AstAssign
398 }
399 }
400 void visit(AstGetcN* nodep) override {
401 // CALLER: str.getc()
402 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
403 if (m_vup && m_vup->prelim()) {
404 // See similar handling in visit_cmp_eq_gt where created
405 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
406 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
407 nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
408 }
409 }
410 void visit(AstGetcRefN* nodep) override {
411 // CALLER: str.getc()
412 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
413 if (m_vup && m_vup->prelim()) {
414 // See similar handling in visit_cmp_eq_gt where created
415 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
416 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
417 nodep->dtypeSetBitSized(8, VSigning::UNSIGNED);
418 }
419 }
420 void visit(AstSubstrN* nodep) override {
421 // CALLER: str.substr()
422 UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
423 if (m_vup && m_vup->prelim()) {
424 // See similar handling in visit_cmp_eq_gt where created
425 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
426 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
427 iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
428 nodep->dtypeSetString();
429 }
430 }
431 void visit(AstCompareNN* nodep) override {
432 // CALLER: str.compare(), str.icompare()
433 // Widths: 32 bit out
434 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
435 if (m_vup->prelim()) {
436 // See similar handling in visit_cmp_eq_gt where created
437 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
438 iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
439 nodep->dtypeSetSigned32();
440 }
441 }
442 void visit(AstAtoN* nodep) override {
443 // CALLER: str.atobin(), atoi(), atohex(), atooct(), atoreal()
444 // Width: 64bit floating point for atoreal(), 32bit out for the others
445 if (m_vup->prelim()) {
446 // See similar handling in visit_cmp_eq_gt where created
447 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
448 if (nodep->format() == AstAtoN::ATOREAL) {
449 nodep->dtypeSetDouble();
450 } else {
451 nodep->dtypeSetSigned32();
452 }
453 }
454 }
455 void visit(AstNToI* nodep) override {
456 // Created here, should be already sized
457 if (m_vup->prelim()) {
458 UASSERT_OBJ(nodep->dtypep(), nodep, "NToI should be sized when created");
459 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
460 }
461 }
462
463 // Widths: Constant, terminal
464 void visit(AstTime* nodep) override { nodep->dtypeSetUInt64(); }
465 void visit(AstTimeD* nodep) override { nodep->dtypeSetDouble(); }
466 void visit(AstTimePrecision* nodep) override { nodep->dtypeSetSigned32(); }
467 void visit(AstTimeUnit* nodep) override {
468 nodep->replaceWith(
469 new AstConst{nodep->fileline(), AstConst::Signed32{}, nodep->timeunit().powerOfTen()});
470 VL_DO_DANGLING(pushDeletep(nodep), nodep);
471 }
472 void visit(AstScopeName* nodep) override {
473 nodep->dtypeSetUInt64(); // A pointer, but not that it matters
474 }
475
476 329 void visit(AstNodeCond* nodep) override {
477 // op = cond ? expr1 : expr2
478 // See IEEE-2012 11.4.11 and Table 11-21.
479 // LHS is self-determined
480 // Width: max(RHS, THS)
481 // Signed: Output signed iff RHS & THS signed (presumed, not in IEEE)
482 // Real: Output real if either expression is real, non-real argument gets converted
483
2/2
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 144 times.
329 if (m_vup->prelim()) { // First stage evaluation
484 // Just once, do the conditional, expect one bit out.
485 185 iterateCheckBool(nodep, "Conditional Test", nodep->condp(), BOTH);
486 // Determine sub expression widths only relying on what's in the subops
487 // CONTEXT_DET determined, but need data type for pattern assignments
488 185 userIterateAndNext(nodep->thenp(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p());
489
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 184 times.
185 userIterateAndNext(nodep->elsep(), WidthVP{m_vup->dtypeNullp(), PRELIM}.p());
490 // Calculate width of this expression.
491 // First call (prelim()) m_vup->width() is probably zero, so we'll return
492 // the size of this subexpression only.
493 // Second call (final()) m_vup->width() is probably the expression size, so
494 // the expression includes the size of the output too.
495 const AstNodeDType* const thenDTypep = nodep->thenp()->dtypep();
496 const AstNodeDType* const elseDTypep = nodep->elsep()->dtypep();
497
2/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 184 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
185 if (nodep->thenp()->isNull() && nodep->elsep()->isNull()) {
498 nodep->dtypep(m_vup->dtypeNullp());
499
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 85 times.
185 } else if (thenDTypep->skipRefp() == elseDTypep->skipRefp()) {
500 // TODO might need a broader equation, use the Castable function?
501 nodep->dtypeFrom(thenDTypep);
502 85 } else if (nodep->thenp()->isClassHandleValue()
503
2/4
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 85 times.
✗ Branch 4 not taken.
85 || nodep->elsep()->isClassHandleValue()) {
504 AstNodeDType* commonClassTypep = nullptr;
505 if (nodep->thenp()->isClassHandleValue() && nodep->elsep()->isClassHandleValue()) {
506 // Get the most-deriving class type that both arguments can be casted to.
507 commonClassTypep
508 = AstNode::getCommonClassTypep(nodep->thenp(), nodep->elsep());
509 }
510 if (commonClassTypep) {
511 nodep->dtypep(commonClassTypep);
512 } else {
513 nodep->v3error("Incompatible types of operands of condition operator: "
514 << thenDTypep->prettyTypeName() << " and "
515 << elseDTypep->prettyTypeName());
516 nodep->dtypeFrom(thenDTypep);
517 }
518
2/4
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
85 } else if (nodep->thenp()->isDouble() || nodep->elsep()->isDouble()) {
519 nodep->dtypeSetDouble();
520
2/4
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
85 } else if (nodep->thenp()->isString() || nodep->elsep()->isString()) {
521 nodep->dtypeSetString();
522 } else {
523
3/6
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 85 times.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
170 const int width = std::max(nodep->thenp()->width(), nodep->elsep()->width());
524 const int mwidth
525
3/6
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 85 times.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
170 = std::max(nodep->thenp()->widthMin(), nodep->elsep()->widthMin());
526 const bool issigned = nodep->thenp()->isSigned() && nodep->elsep()->isSigned();
527 170 nodep->dtypeSetLogicUnsized(width, mwidth, VSigning::fromBool(issigned));
528 }
529 }
530
2/2
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 144 times.
329 if (m_vup->final()) {
531 185 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
532 AstNodeDType* const subDTypep = expDTypep;
533 nodep->dtypep(expDTypep);
534 // Error report and change sizes for suboperands of this node.
535 185 iterateCheck(nodep, "Conditional True", nodep->thenp(), CONTEXT_DET, FINAL, subDTypep,
536 EXTEND_EXP);
537 185 iterateCheck(nodep, "Conditional False", nodep->elsep(), CONTEXT_DET, FINAL, subDTypep,
538 EXTEND_EXP);
539 }
540 329 }
541 18458 void visit(AstConcat* nodep) override {
542 // Real: Not allowed (assumed)
543 // Signed: unsigned output, input either (assumed)
544 // IEEE-2012 Table 11-21, and 11.8.1:
545 // LHS, RHS is self-determined
546 // signed: Unsigned (11.8.1)
547 // width: LHS + RHS
548
2/2
✓ Branch 0 taken 9149 times.
✓ Branch 1 taken 9309 times.
18458 AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
549
1/2
✓ Branch 1 taken 9149 times.
✗ Branch 2 not taken.
36916 userIterate(vdtypep, WidthVP{SELF, BOTH}.p());
550 // Conversions
551 if (const AstDynArrayDType* const adtypep = VN_CAST(vdtypep, DynArrayDType)) {
552 // IEEE 1800-2023 10.10 requires looking at argument data type
553 // to determine if value or push
554 userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p());
555 userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p());
556 // Queue "element 0" is lhsp, so we need to swap arguments
557 const bool lhsIsValue
558 = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr)
559 .isAssignable();
560 const bool rhsIsValue
561 = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr)
562 .isAssignable();
563 AstConsDynArray* const newp
564 = new AstConsDynArray{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(),
565 lhsIsValue, nodep->lhsp()->unlinkFrBack()};
566 nodep->replaceWith(newp);
567 VL_DO_DANGLING(pushDeletep(nodep), nodep);
568 userIterateChildren(newp, m_vup);
569 return;
570 }
571 if (const AstQueueDType* const adtypep = VN_CAST(vdtypep, QueueDType)) {
572 // IEEE 1800-2023 10.10 requires looking at argument data type
573 // to determine if value or push
574 userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, PRELIM}.p());
575 userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, PRELIM}.p());
576 const bool lhsIsValue
577 = AstNode::computeCastable(adtypep->subDTypep(), nodep->lhsp()->dtypep(), nullptr)
578 .isAssignable();
579 const bool rhsIsValue
580 = AstNode::computeCastable(adtypep->subDTypep(), nodep->rhsp()->dtypep(), nullptr)
581 .isAssignable();
582 AstConsQueue* const newp
583 = new AstConsQueue{nodep->fileline(), rhsIsValue, nodep->rhsp()->unlinkFrBack(),
584 lhsIsValue, nodep->lhsp()->unlinkFrBack()};
585 nodep->replaceWith(newp);
586 VL_DO_DANGLING(pushDeletep(nodep), nodep);
587 userIterateChildren(newp, m_vup);
588 return;
589 }
590 if (VN_IS(vdtypep, UnpackArrayDType)) {
591 auto* const newp = new AstPattern{nodep->fileline(), nullptr};
592 patConcatConvertRecurse(newp, nodep);
593 nodep->replaceWith(newp);
594 VL_DO_DANGLING(pushDeletep(nodep), nodep);
595 userIterate(newp, m_vup);
596 return;
597 }
598
599 // Concat handling
600
2/2
✓ Branch 0 taken 9309 times.
✓ Branch 1 taken 9149 times.
18458 if (m_vup->prelim()) {
601 if (VN_IS(vdtypep, AssocArrayDType) //
602 || VN_IS(vdtypep, DynArrayDType) //
603 || VN_IS(vdtypep, QueueDType)) {
604 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Concatenation to form "
605 << vdtypep->prettyDTypeNameQ() << " data type");
606 }
607
608
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9309 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
9309 if (vdtypep && vdtypep->isString()) {
609 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
610 iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
611 nodep->dtypeSetString();
612 } else {
613 9309 iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
614 9309 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
615
2/4
✓ Branch 0 taken 9309 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9309 times.
✗ Branch 3 not taken.
37236 nodep->dtypeSetLogicUnsized(nodep->lhsp()->width() + nodep->rhsp()->width(),
616 nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(),
617 VSigning::UNSIGNED);
618 }
619 // Cleanup zero width Verilog2001 {x,{0{foo}}} now,
620 // otherwise having width(0) will cause later assertions to fire
621 if (const AstReplicate* const repp = VN_CAST(nodep->lhsp(), Replicate)) {
622 if (repp->width() == 0) { // Keep rhs
623 nodep->replaceWith(nodep->rhsp()->unlinkFrBack());
624 VL_DO_DANGLING(pushDeletep(nodep), nodep);
625 return;
626 }
627 }
628 if (const AstReplicate* const repp = VN_CAST(nodep->rhsp(), Replicate)) {
629 if (repp->width() == 0) { // Keep lhs
630 nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
631 VL_DO_DANGLING(pushDeletep(nodep), nodep);
632 return;
633 }
634 }
635 }
636
2/2
✓ Branch 0 taken 9309 times.
✓ Branch 1 taken 9149 times.
18458 if (m_vup->final()) {
637
2/4
✓ Branch 1 taken 9309 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9309 times.
✗ Branch 5 not taken.
9309 if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
638 AstNodeExpr* lhsp = nodep->lhsp()->unlinkFrBack();
639 AstNodeExpr* rhsp = nodep->rhsp()->unlinkFrBack();
640 if (!lhsp->isString()) lhsp = new AstCvtPackString{lhsp->fileline(), lhsp};
641 if (!rhsp->isString()) rhsp = new AstCvtPackString{rhsp->fileline(), rhsp};
642 AstNode* const newp = new AstConcatN{nodep->fileline(), lhsp, rhsp};
643 nodep->replaceWith(newp);
644 VL_DO_DANGLING(pushDeletep(nodep), nodep);
645 return;
646 }
647 if (!nodep->dtypep()->widthSized()) {
648 // See also error in V3Number
649 nodeForUnsizedWarning(nodep)->v3warn(
650 WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
651 }
652 }
653 }
654 void visit(AstConcatN* nodep) override {
655 if (nodep->didWidth()) return;
656 // String concatenate.
657 // Already did AstConcat simplifications
658 if (m_vup->prelim()) {
659 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
660 iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
661 nodep->dtypeSetString();
662 }
663 if (m_vup->final()) {
664 nodep->didWidth(true);
665 if (!nodep->dtypep()->widthSized()) {
666 // See also error in V3Number
667 nodeForUnsizedWarning(nodep)->v3warn(
668 WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations.");
669 }
670 }
671 }
672 void visit(AstDefaultDisable* nodep) override {
673 assertAtStatement(nodep);
674 // it's like an if() condition.
675 iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH);
676 }
677 void visit(AstDelay* nodep) override {
678 if (VN_IS(m_procedurep, Final)) {
679 nodep->v3error("Delays are not legal in final blocks (IEEE 1800-2023 9.2.3)");
680 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
681 return;
682 }
683 if (VN_IS(m_ftaskp, Func)) {
684 nodep->v3error("Delays are not legal in functions. Suggest use a task "
685 "(IEEE 1800-2023 13.4.4)");
686 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
687 return;
688 }
689 if (nodep->fileline()->timingOn()) {
690 if (v3Global.opt.timing().isSetTrue()) {
691 iterateCheckDelay(nodep, "delay", nodep->lhsp(), BOTH);
692 iterateAndNextNull(nodep->stmtsp());
693 return;
694 } else if (v3Global.opt.timing().isSetFalse()) {
695 nodep->v3warn(STMTDLY, "Ignoring delay on this statement due to --no-timing");
696 } else {
697 nodep->v3warn(
698 E_NEEDTIMINGOPT,
699 "Use --timing or --no-timing to specify how delays should be handled");
700 }
701 }
702 if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext());
703 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
704 }
705 void visit(AstFork* nodep) override {
706 if (VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) {
707 nodep->v3error("Only fork .. join_none is legal in functions. "
708 "(IEEE 1800-2023 13.4.4)");
709 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
710 return;
711 }
712 if (!nodep->fileline()->timingOn()
713 // With no statements, begin is identical
714 || !nodep->stmtsp()
715 || (!v3Global.opt.timing().isSetTrue() // If no --timing
716 && (v3Global.opt.bboxUnsup()
717 // With one statement and no timing, a begin block does as good as a
718 // fork/join or join_any
719 || (!nodep->stmtsp()->nextp() && !nodep->joinType().joinNone())))) {
720 AstNode* stmtsp = nullptr;
721 if (nodep->stmtsp()) stmtsp = nodep->stmtsp()->unlinkFrBack();
722 AstBegin* const newp = new AstBegin{nodep->fileline(), nodep->name(), stmtsp};
723 nodep->replaceWith(newp);
724 VL_DO_DANGLING(nodep->deleteTree(), nodep);
725 } else if (v3Global.opt.timing().isSetTrue()) {
726 iterateChildren(nodep);
727 } else if (v3Global.opt.timing().isSetFalse()) {
728 nodep->v3warn(E_NOTIMING, "Fork statements require --timing");
729 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
730 } else {
731 nodep->v3warn(E_NEEDTIMINGOPT,
732 "Use --timing or --no-timing to specify how forks should be handled");
733 }
734 }
735 void visit(AstDisableFork* nodep) override {
736 if (nodep->fileline()->timingOn()) {
737 if (v3Global.opt.timing().isSetFalse()) {
738 nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing");
739 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
740 } else if (!v3Global.opt.timing().isSetTrue()) {
741 nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
742 << "disable fork should be handled");
743 }
744 }
745 }
746 void visit(AstWaitFork* nodep) override {
747 if (nodep->fileline()->timingOn()) {
748 if (v3Global.opt.timing().isSetFalse()) {
749 nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing");
750 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
751 } else if (!v3Global.opt.timing().isSetTrue()) {
752 nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
753 << "disable fork should be handled");
754 }
755 }
756 }
757 void visit(AstToLowerN* nodep) override {
758 if (m_vup->prelim()) {
759 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
760 nodep->dtypeSetString();
761 }
762 }
763 void visit(AstToUpperN* nodep) override {
764 if (m_vup->prelim()) {
765 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
766 nodep->dtypeSetString();
767 }
768 }
769 5741 void visit(AstReplicate* nodep) override {
770 // IEEE-2012 Table 11-21:
771 // LHS, RHS is self-determined
772 // width: value(LHS) * width(RHS)
773
2/2
✓ Branch 0 taken 2957 times.
✓ Branch 1 taken 2784 times.
5741 if (m_vup->prelim()) {
774 2957 iterateCheckSizedSelf(nodep, "RHS", nodep->countp(), SELF, BOTH);
775 V3Const::constifyParamsNoWarnEdit(nodep->countp()); // rhsp may change
776
777 uint32_t times = 1;
778
779 const AstConst* const constp = VN_CAST(nodep->countp(), Const);
780 if (constp) times = constp->toUInt();
781
782
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 2657 times.
2957 AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
783 if (VN_IS(vdtypep, QueueDType) || VN_IS(vdtypep, DynArrayDType)
784 || VN_IS(vdtypep, UnpackArrayDType)) {
785 if (times != 1) {
786 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-1 replication to form "
787 << vdtypep->prettyDTypeNameQ()
788 << " data type");
789 }
790 if (VN_IS(nodep->srcp(), Concat)) {
791 // Convert to concat directly, and visit(AstConst) will convert.
792 // Don't iterate lhsp as SELF, the potential Concat below needs
793 // the adtypep passed down to recognize the QueueDType
794 userIterateAndNext(nodep->srcp(), WidthVP{vdtypep, BOTH}.p());
795 nodep->replaceWith(nodep->srcp()->unlinkFrBack());
796 VL_DO_DANGLING(pushDeletep(nodep), nodep);
797 return;
798 } else { // int a[] = {lhs} -> same as '{lhs}
799 auto* const newp = new AstPattern{
800 nodep->fileline(),
801 new AstPatMember{nodep->srcp()->fileline(), nodep->srcp()->unlinkFrBack(),
802 nullptr, nullptr}};
803 nodep->replaceWith(newp);
804 VL_DO_DANGLING(pushDeletep(nodep), nodep);
805 userIterate(newp, m_vup);
806 return;
807 }
808 }
809 if (VN_IS(vdtypep, AssocArrayDType)) {
810 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Replication to form "
811 << vdtypep->prettyDTypeNameQ() << " data type");
812 }
813
3/4
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 2657 times.
✓ Branch 3 taken 300 times.
✗ Branch 4 not taken.
2957 if (vdtypep && vdtypep->isString()) {
814 iterateCheckString(nodep, "LHS", nodep->srcp(), BOTH);
815 } else {
816 2957 iterateCheckSizedSelf(nodep, "LHS", nodep->srcp(), SELF, BOTH);
817 }
818
819
4/6
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 2657 times.
✓ Branch 3 taken 300 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2957 times.
✗ Branch 7 not taken.
2957 if ((vdtypep && vdtypep->isString()) || nodep->srcp()->isString()) {
820 AstNode* const newp
821 = new AstReplicateN{nodep->fileline(), nodep->srcp()->unlinkFrBack(),
822 nodep->countp()->unlinkFrBack()};
823 nodep->replaceWith(newp);
824 VL_DO_DANGLING(pushDeletep(nodep), nodep);
825 return;
826 } else {
827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2957 times.
2957 if (!constp) nodep->v3error("Replication value isn't a constant.");
828 if (times == 0
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2957 times.
2957 && !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up.
830 nodep->v3warn(ZEROREPL,
831 "Replication value of 0 is only legal under a concatenation"
832 " (IEEE 1800-2023 11.4.12.1)");
833 times = 1; // Set to 1, so we can continue looking for errors
834 }
835
1/2
✓ Branch 0 taken 2957 times.
✗ Branch 1 not taken.
8871 nodep->dtypeSetLogicUnsized((nodep->srcp()->width() * times),
836
1/2
✓ Branch 0 taken 2957 times.
✗ Branch 1 not taken.
2957 (nodep->srcp()->widthMin() * times),
837 VSigning::UNSIGNED);
838 }
839 }
840
2/2
✓ Branch 0 taken 2957 times.
✓ Branch 1 taken 2784 times.
5741 if (m_vup->final()) {
841 if (!nodep->dtypep()->widthSized()) {
842 // See also error in V3Number
843 nodeForUnsizedWarning(nodep)->v3warn(
844 WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
845 }
846 }
847 }
848 void visit(AstReplicateN* nodep) override {
849 // Replicate with string
850 if (m_vup->prelim()) {
851 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
852 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
853 V3Const::constifyParamsNoWarnEdit(nodep->rhsp()); // rhsp may change
854 nodep->dtypeSetString();
855 }
856 if (m_vup->final()) {
857 if (!nodep->dtypep()->widthSized()) {
858 // See also error in V3Number
859 nodeForUnsizedWarning(nodep)->v3warn(
860 WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications.");
861 }
862 }
863 }
864 void visit(AstNodeDistBiop* nodep) override {
865 if (m_vup->prelim()) { // First stage evaluation
866 iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH);
867 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
868 nodep->dtypeSetSigned32();
869 }
870 }
871 void visit(AstNodeDistTriop* nodep) override {
872 if (m_vup->prelim()) { // First stage evaluation
873 iterateCheckSigned32(nodep, "seed", nodep->lhsp(), BOTH);
874 iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
875 iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
876 nodep->dtypeSetSigned32();
877 }
878 }
879 void visit(AstNodeStream* nodep) override {
880 if (m_vup->prelim()) {
881 iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
882 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
883 V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
884 if (const AstConst* const constp = VN_CAST(nodep->rhsp(), Const)) {
885 if (constp->toUInt() == 0) nodep->v3error("Slice size cannot be zero.");
886 } else {
887 nodep->v3error("Slice size isn't a constant or basic data type.");
888 }
889 const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefToEnump();
890 if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType)
891 || VN_IS(lhsDtypep, UnpackArrayDType)) {
892 nodep->dtypeSetStream();
893 } else if (lhsDtypep->isCompound()) {
894 nodep->v3warn(E_UNSUPPORTED,
895 "Unsupported: Stream operation on a variable of a type "
896 << lhsDtypep->prettyDTypeNameQ());
897 nodep->dtypeSetStream();
898 } else {
899 nodep->dtypeSetLogicUnsized(nodep->lhsp()->width(), nodep->lhsp()->widthMin(),
900 VSigning::UNSIGNED);
901 }
902 }
903 if (m_vup->final()) {
904 if (!nodep->dtypep()->widthSized()) {
905 // See also error in V3Number
906 nodeForUnsizedWarning(nodep)->v3warn(
907 WIDTHCONCAT, "Unsized numbers/parameters not allowed in streams.");
908 }
909 }
910 }
911 1680 void visit(AstRange* nodep) override {
912 // Real: Not allowed
913 // Signed: unsigned output, input either
914 // Convert all range values to constants
915
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1680 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
1680 UINFO(6, "RANGE " << nodep << endl);
916 V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node
917 V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node
918
1/2
✓ Branch 2 taken 1680 times.
✗ Branch 3 not taken.
1680 checkConstantOrReplace(nodep->leftp(), "left side of bit range isn't a constant");
919
1/2
✓ Branch 2 taken 1680 times.
✗ Branch 3 not taken.
1680 checkConstantOrReplace(nodep->rightp(), "right side of bit range isn't a constant");
920
1/2
✓ Branch 0 taken 1680 times.
✗ Branch 1 not taken.
1680 if (m_vup->prelim()) {
921 // Don't need to iterate because V3Const already constified
922 1680 const int width = nodep->elementsConst();
923
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1680 times.
1680 if (width > (1 << 28)) {
924 nodep->v3error("Width of bit range is huge; vector of over 1 billion bits: 0x"
925 << std::hex << width << std::dec);
926 }
927 // Note width() not set on range; use elementsConst()
928 1680 if (nodep->ascending() && !VN_IS(nodep->backp(), UnpackArrayDType)
929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1680 times.
1680 && !VN_IS(nodep->backp(), Cell)) { // For cells we warn in V3Inst
930 nodep->v3warn(ASCRANGE, "Ascending bit range vector: left < right of bit range: ["
931 << nodep->leftConst() << ":" << nodep->rightConst()
932 << "]");
933 }
934 }
935 1680 }
936
937
1/2
✓ Branch 0 taken 13853 times.
✗ Branch 1 not taken.
13853 void visit(AstSel* nodep) override {
938 // Signed: always unsigned; Real: Not allowed
939 // LSB is self-determined (IEEE 2012 11.5.1)
940 // We also use SELs to shorten a signed constant etc, in this case they are signed.
941
1/2
✓ Branch 0 taken 13853 times.
✗ Branch 1 not taken.
13853 if (nodep->didWidth()) return;
942
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13853 times.
13853 UASSERT_OBJ(m_vup, nodep, "Select under an unexpected context");
943
2/2
✓ Branch 0 taken 8360 times.
✓ Branch 1 taken 5493 times.
13853 if (m_vup->prelim()) {
944
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8360 times.
8360 if (debug() >= 9) nodep->dumpTree("- selWidth: ");
945 8360 userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p());
946 8360 userIterateAndNext(nodep->lsbp(), WidthVP{SELF, PRELIM}.p());
947 8360 checkCvtUS(nodep->fromp());
948 8360 iterateCheckSizedSelf(nodep, "Select Width", nodep->widthp(), SELF, BOTH);
949 8360 iterateCheckSizedSelf(nodep, "Select LHS", nodep->fromp(), SELF, BOTH);
950 V3Const::constifyParamsEdit(nodep->widthp()); // widthp may change
951 const AstConst* const widthConstp = VN_CAST(nodep->widthp(), Const);
952 if (!widthConstp) {
953 nodep->v3error(
954 "Width of bit extract isn't a constant"); // Impossible? // LCOV_EXCL_LINE
955 nodep->dtypeSetBit();
956 return;
957 }
958 int width = nodep->widthConst();
959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8360 times.
8360 if (width <= 0) {
960 nodep->v3error("Width of bit extract must be positive (IEEE 1800-2023 11.5.1)");
961 nodep->dtypeSetBit();
962 return;
963 }
964
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8360 times.
8360 UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
965
1/2
✓ Branch 1 taken 8360 times.
✗ Branch 2 not taken.
16720 if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) {
966 // Likely impossible given above width check
967 nodep->v3warn(E_UNSUPPORTED,
968 "Unsupported: left < right of bit extract: " // LCOV_EXCL_LINE
969 << nodep->msbConst() << "<" << nodep->lsbConst());
970 width = (nodep->lsbConst() - nodep->msbConst() + 1);
971 nodep->dtypeSetLogicSized(width, VSigning::UNSIGNED);
972 pushDeletep(nodep->widthp());
973 nodep->widthp()->replaceWith(new AstConst(nodep->widthp()->fileline(), width));
974 pushDeletep(nodep->lsbp());
975 nodep->lsbp()->replaceWith(new AstConst{nodep->lsbp()->fileline(), 0});
976 }
977 // We're extracting, so just make sure the expression is at least wide enough.
978
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8360 times.
8360 if (nodep->fromp()->width() < width) {
979 nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
980 << nodep->fromp()->width() << " bit number");
981 // Extend it.
982 AstNodeDType* const subDTypep
983 = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
984 widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
985 false /*noerror*/);
986 }
987 // Check bit indexes.
988 // What is the MSB? We want the true MSB, not one starting at
989 // 0, because a 4 bit index is required to look at a one-bit
990 // variable[15:15] and 5 bits for [15:-2]
991
1/2
✓ Branch 0 taken 8360 times.
✗ Branch 1 not taken.
8360 int frommsb = nodep->fromp()->width() - 1;
992 int fromlsb = 0;
993 const int elw = nodep->declElWidth(); // Must adjust to tell user bit ranges
994
1/2
✓ Branch 0 taken 8360 times.
✗ Branch 1 not taken.
8360 if (nodep->declRange().ranged()) {
995 8360 frommsb = nodep->declRange().hiMaxSelect() * elw
996 8360 + (elw - 1); // Corrected for negative lsb
997 8360 fromlsb = nodep->declRange().lo() * elw;
998 } else {
999 // nodep->v3fatalSrc("Should have been declRanged in V3WidthSel");
1000 }
1001 8360 const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit
1002 AstNodeDType* const selwidthDTypep
1003 8360 = nodep->findLogicDType(selwidth, selwidth, nodep->lsbp()->dtypep()->numeric());
1004 8360 userIterateAndNext(nodep->fromp(), WidthVP{SELF, FINAL}.p());
1005 8360 userIterateAndNext(nodep->lsbp(), WidthVP{SELF, FINAL}.p());
1006
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8360 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
8360 if (widthBad(nodep->lsbp(), selwidthDTypep) && nodep->lsbp()->width() != 32) {
1007 if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
1008 nodep->v3widthWarn(
1009 (selwidth / elw), (nodep->lsbp()->width() / elw),
1010 "Bit extraction of var["
1011 << (frommsb / elw) << ":" << (fromlsb / elw) << "] requires "
1012 << (selwidth / elw) << " bit index, not "
1013 << (nodep->lsbp()->width() / elw)
1014 << (nodep->lsbp()->width() != nodep->lsbp()->widthMin()
1015 ? " or " + cvtToStr(nodep->lsbp()->widthMin() / elw)
1016 : "")
1017 << " bits.");
1018 UINFO(1, " Related node: " << nodep << endl);
1019 }
1020 }
1021
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8360 times.
8360 if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() > frommsb) {
1022 // See also warning in V3Const
1023 // We need to check here, because the widthCheckSized may silently
1024 // add another SEL which will lose the out-of-range check
1025 //
1026 // We don't want to trigger an error here if we are just
1027 // evaluating type sizes for a generate block condition. We
1028 // should only trigger the error if the out-of-range access is
1029 // actually generated.
1030 if (m_doGenerate) {
1031 UINFO(5, "Selection index out of range inside generate." << endl);
1032 } else {
1033 nodep->v3warn(SELRANGE, "Selection index out of range: "
1034 << nodep->msbConst() << ":" << nodep->lsbConst()
1035 << " outside " << frommsb << ":" << fromlsb);
1036 UINFO(1, " Related node: " << nodep << endl);
1037 }
1038 // Extend it.
1039 const int extendTo = nodep->msbConst() + 1;
1040 AstNodeDType* const subDTypep = nodep->findLogicDType(
1041 extendTo, extendTo, nodep->fromp()->dtypep()->numeric());
1042 widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
1043 false /*noerror*/);
1044 }
1045 // iterate FINAL is two blocks above
1046 //
1047 // If we have a width problem with GENERATE etc, this will reduce
1048 // it down and mask it, so we have no chance of finding a real
1049 // error in the future. So don't do this for them.
1050
1/2
✓ Branch 0 taken 8360 times.
✗ Branch 1 not taken.
8360 if (!m_doGenerate) {
1051 // lsbp() must be self-determined, however for performance
1052 // we want the select to be truncated to fit within the
1053 // maximum select range, e.g. turn Xs outside of the select
1054 // into something fast which pulls from within the array.
1055 8360 widthCheckSized(nodep, "Extract Range", nodep->lsbp(), selwidthDTypep, EXTEND_EXP,
1056 false /*NOWARN*/);
1057 }
1058 }
1059 }
1060
1061 void visit(AstArraySel* nodep) override {
1062 // Signed/Real: Output signed iff LHS signed/real; binary operator
1063 // Note by contrast, bit extract selects are unsigned
1064 // LSB is self-determined (IEEE 2012 11.5.1)
1065 if (m_vup->prelim()) {
1066 iterateCheckSizedSelf(nodep, "Bit select", nodep->bitp(), SELF, BOTH);
1067 userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1068 //
1069 int frommsb;
1070 int fromlsb;
1071 const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1072 if (const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
1073 frommsb = adtypep->hi();
1074 fromlsb = adtypep->lo();
1075 if (fromlsb > frommsb) {
1076 const int t = frommsb;
1077 frommsb = fromlsb;
1078 fromlsb = t;
1079 }
1080 // However, if the lsb<0 we may go negative, so need more bits!
1081 if (fromlsb < 0) frommsb += -fromlsb;
1082 nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference
1083 } else {
1084 // Note PackArrayDType doesn't use an ArraySel but a normal Sel.
1085 UINFO(1, " Related dtype: " << fromDtp << endl);
1086 nodep->v3fatalSrc("Array reference exceeds dimension of array");
1087 frommsb = fromlsb = 0;
1088 }
1089 const int selwidth = V3Number::log2b(frommsb + 1 - 1) + 1; // Width to address a bit
1090 AstNodeDType* const selwidthDTypep
1091 = nodep->findLogicDType(selwidth, selwidth, nodep->bitp()->dtypep()->numeric());
1092 if (widthBad(nodep->bitp(), selwidthDTypep) && nodep->bitp()->width() != 32) {
1093 nodep->v3widthWarn(selwidth, nodep->bitp()->width(),
1094 "Bit extraction of array["
1095 << frommsb << ":" << fromlsb << "] requires " << selwidth
1096 << " bit index, not " << nodep->bitp()->width()
1097 << (nodep->bitp()->width() != nodep->bitp()->widthMin()
1098 ? " or " + cvtToStr(nodep->bitp()->widthMin())
1099 : "")
1100 << " bits.");
1101 if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) {
1102 UINFO(1, " Related node: " << nodep << endl);
1103 UINFO(1, " Related dtype: " << nodep->dtypep() << endl);
1104 }
1105 }
1106 if (!m_doGenerate) {
1107 // Must check bounds before adding a select that truncates the bound
1108 // Note we've already subtracted off LSB
1109 if (VN_IS(nodep->bitp(), Const)
1110 && (VN_AS(nodep->bitp(), Const)->toSInt() > (frommsb - fromlsb)
1111 || VN_AS(nodep->bitp(), Const)->toSInt() < 0)) {
1112 nodep->v3warn(SELRANGE,
1113 "Selection index out of range: "
1114 << (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb)
1115 << " outside " << frommsb << ":" << fromlsb);
1116 UINFO(1, " Related node: " << nodep << endl);
1117 }
1118 widthCheckSized(nodep, "Extract Range", nodep->bitp(), selwidthDTypep, EXTEND_EXP,
1119 false /*NOWARN*/);
1120 }
1121 }
1122 }
1123
1124 void visit(AstAssocSel* nodep) override {
1125 // Signed/Real: Output type based on array-declared type; binary operator
1126 if (m_vup->prelim()) {
1127 const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1128 const AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType);
1129 if (!adtypep) {
1130 UINFO(1, " Related dtype: " << fromDtp << endl);
1131 nodep->v3fatalSrc("Associative array reference is not to associative array");
1132 }
1133 iterateCheckTyped(nodep, "Associative select", nodep->bitp(), adtypep->keyDTypep(),
1134 BOTH);
1135 nodep->dtypeFrom(adtypep->subDTypep());
1136 }
1137 }
1138
1139 void visit(AstWildcardSel* nodep) override {
1140 // Signed/Real: Output type based on array-declared type; binary operator
1141 if (m_vup->prelim()) {
1142 const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1143 const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType);
1144 if (!adtypep) {
1145 UINFO(1, " Related dtype: " << fromDtp << endl);
1146 nodep->v3fatalSrc("Wildcard array reference is not to wildcard array");
1147 }
1148 const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp();
1149 if (!basicp
1150 || (basicp->keyword() != VBasicDTypeKwd::STRING
1151 && !basicp->keyword().isIntNumeric())) {
1152 nodep->v3error("Wildcard index must be integral (IEEE 1800-2023 7.8.1)");
1153 }
1154 iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(),
1155 adtypep->findStringDType(), BOTH);
1156 nodep->dtypeFrom(adtypep->subDTypep());
1157 }
1158 }
1159
1160 void visit(AstSliceSel* nodep) override {
1161 // Always creates as output an unpacked array
1162 if (m_vup->prelim()) {
1163 userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1164 //
1165 // Array indices are always constant
1166 const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
1167 const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType);
1168 if (!adtypep) {
1169 UINFO(1, " Related dtype: " << fromDtp << endl);
1170 nodep->v3fatalSrc("Packed array reference exceeds dimension of array");
1171 }
1172 // Build new array Dtype based on the original's base type, but with new bounds
1173 AstNodeDType* const newDtp
1174 = new AstUnpackArrayDType{nodep->fileline(), adtypep->subDTypep(),
1175 new AstRange{nodep->fileline(), nodep->declRange()}};
1176 v3Global.rootp()->typeTablep()->addTypesp(newDtp);
1177 nodep->dtypeFrom(newDtp);
1178
1179 if (!m_doGenerate) {
1180 // Must check bounds before adding a select that truncates the bound
1181 // Note we've already subtracted off LSB
1182 const int subtracted = adtypep->declRange().lo();
1183 // Add subtracted value to get the original range
1184 const VNumRange declRange{nodep->declRange().hi() + subtracted,
1185 nodep->declRange().lo() + subtracted,
1186 nodep->declRange().ascending()};
1187 if ((declRange.hi() > adtypep->declRange().hi())
1188 || declRange.lo() < adtypep->declRange().lo()) {
1189 // Other simulators warn too
1190 nodep->v3error("Slice selection index '" << declRange << "'"
1191 << " outside data type's '"
1192 << adtypep->declRange() << "'");
1193 } else if ((declRange.ascending() != adtypep->declRange().ascending())
1194 && declRange.hi() != declRange.lo()) {
1195 nodep->v3error("Slice selection '"
1196 << declRange << "'"
1197 << " has reversed range order versus data type's '"
1198 << adtypep->declRange() << "'");
1199 }
1200 }
1201 }
1202 }
1203
1204 2524 void visit(AstSelBit* nodep) override {
1205 // Just a quick check as after V3Param these nodes instead are AstSel's
1206 2524 userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1207 2524 userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1208 2524 userIterateAndNext(nodep->thsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1209 2524 userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1210 2524 AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1211
1/2
✓ Branch 0 taken 2524 times.
✗ Branch 1 not taken.
2524 if (selp != nodep) {
1212 VL_DANGLING(nodep);
1213 2524 userIterate(selp, m_vup);
1214 2524 return;
1215 }
1216 nodep->v3fatalSrc("AstSelBit should disappear after widthSel");
1217 }
1218 3568 void visit(AstSelExtract* nodep) override {
1219 // Just a quick check as after V3Param these nodes instead are AstSel's
1220 3568 userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1221 3568 userIterateAndNext(nodep->leftp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1222 3568 userIterateAndNext(nodep->rightp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1223 3568 userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1224 3568 AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1225
1/2
✓ Branch 0 taken 3568 times.
✗ Branch 1 not taken.
3568 if (selp != nodep) {
1226 nodep = nullptr;
1227 3568 userIterate(selp, m_vup);
1228 3568 return;
1229 }
1230 nodep->v3fatalSrc("AstSelExtract should disappear after widthSel");
1231 }
1232 void visit(AstSelPlus* nodep) override {
1233 userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1234 userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1235 userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1236 userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1237 AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1238 if (selp != nodep) {
1239 nodep = nullptr;
1240 userIterate(selp, m_vup);
1241 return;
1242 }
1243 nodep->v3fatalSrc("AstSelPlus should disappear after widthSel");
1244 }
1245 void visit(AstSelMinus* nodep) override {
1246 userIterateAndNext(nodep->fromp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1247 userIterateAndNext(nodep->bitp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1248 userIterateAndNext(nodep->widthp(), WidthVP{CONTEXT_DET, PRELIM}.p()); // FINAL in AstSel
1249 userIterateAndNext(nodep->attrp(), WidthVP{SELF, BOTH}.p());
1250 AstNode* const selp = V3Width::widthSelNoIterEdit(nodep);
1251 if (selp != nodep) {
1252 nodep = nullptr;
1253 userIterate(selp, m_vup);
1254 return;
1255 }
1256 nodep->v3fatalSrc("AstSelMinus should disappear after widthSel");
1257 }
1258
1259 void visit(AstExtend* nodep) override {
1260 // Typically created by this process, so we know width from here down is correct.
1261 // Exception is extend added by V3WidthSel - those need iteration
1262 if (nodep->didWidthAndSet()) return;
1263 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1264 }
1265 void visit(AstExtendS* nodep) override {
1266 // Typically created by this process, so we know width from here down is correct.
1267 // Exception is extend added by V3WidthSel - those need iteration
1268 if (nodep->didWidthAndSet()) return;
1269 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1270 }
1271 57465 void visit(AstConst* nodep) override {
1272 // The node got setup with the signed/real state of the node.
1273 // However a later operation may have changed the node->signed w/o changing
1274 // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned());
1275
2/2
✓ Branch 0 taken 24883 times.
✓ Branch 1 taken 32582 times.
57465 if (nodep->didWidthAndSet()) return;
1276
2/4
✓ Branch 0 taken 24883 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24883 times.
✗ Branch 3 not taken.
24883 if (m_vup && m_vup->prelim()) {
1277 if (VN_IS(nodep->dtypep()->skipRefToEnump(), EnumDType)) {
1278 // Assume this constant was properly casted earlier
1279 // (Otherwise it couldn't have an enum data type)
1280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24883 times.
24883 } else if (nodep->num().isString()) {
1281 nodep->dtypeSetString();
1282
2/2
✓ Branch 0 taken 5771 times.
✓ Branch 1 taken 19112 times.
24883 } else if (nodep->num().sized()) {
1283 5771 nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width());
1284 } else {
1285 19112 nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin());
1286 }
1287 }
1288 // We don't size the constant until we commit the widths, as need parameters
1289 // to remain unsized, and numbers to remain unsized to avoid backp() warnings
1290 }
1291 void visit(AstEmptyQueue* nodep) override {
1292 nodep->dtypeSetEmptyQueue();
1293 if (!VN_IS(nodep->backp(), Assign) && !VN_IS(nodep->backp(), Var)) {
1294 nodep->v3warn(E_UNSUPPORTED,
1295 "Unsupported/Illegal: empty queue ('{}') in this context");
1296 }
1297 }
1298 void visit(AstFell* nodep) override {
1299 if (m_vup->prelim()) {
1300 iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1301 userIterate(nodep->sentreep(), nullptr);
1302 nodep->dtypeSetBit();
1303 }
1304 }
1305 void visit(AstPast* nodep) override {
1306 if (m_vup->prelim()) {
1307 iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1308 nodep->dtypeFrom(nodep->exprp());
1309 if (nodep->ticksp()) {
1310 iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH);
1311 V3Const::constifyParamsEdit(nodep->ticksp()); // ticksp may change
1312 const AstConst* const constp = VN_CAST(nodep->ticksp(), Const);
1313 if (!constp) {
1314 nodep->v3error("$past tick value must be constant (IEEE 1800-2023 16.9.3)");
1315 nodep->ticksp()->unlinkFrBack()->deleteTree();
1316 } else if (constp->toSInt() < 1) {
1317 constp->v3error("$past tick value must be >= 1 (IEEE 1800-2023 16.9.3)");
1318 nodep->ticksp()->unlinkFrBack()->deleteTree();
1319 } else {
1320 if (constp->toSInt() > 10) {
1321 constp->v3warn(TICKCOUNT, "$past tick value of "
1322 << constp->toSInt()
1323 << " may have a large performance cost");
1324 }
1325 }
1326 }
1327 }
1328 }
1329 void visit(AstRose* nodep) override {
1330 if (m_vup->prelim()) {
1331 iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1332 userIterate(nodep->sentreep(), nullptr);
1333 nodep->dtypeSetBit();
1334 }
1335 }
1336
1337 void visit(AstSampled* nodep) override {
1338 if (m_vup->prelim()) {
1339 iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1340 nodep->dtypeFrom(nodep->exprp());
1341 }
1342 }
1343
1344 void visit(AstStable* nodep) override {
1345 if (m_vup->prelim()) {
1346 iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
1347 userIterate(nodep->sentreep(), nullptr);
1348 nodep->dtypeSetBit();
1349 }
1350 }
1351
1352 void visit(AstImplication* nodep) override {
1353 if (m_vup->prelim()) {
1354 iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
1355 iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
1356 nodep->dtypeSetBit();
1357 }
1358 }
1359
1360 void visit(AstRand* nodep) override {
1361 if (m_vup->prelim()) {
1362 if (nodep->urandom()) {
1363 nodep->dtypeSetUInt32(); // Says the spec
1364 } else {
1365 nodep->dtypeSetSigned32(); // Says the spec
1366 }
1367 if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH);
1368 }
1369 }
1370 void visit(AstURandomRange* nodep) override {
1371 if (m_vup->prelim()) {
1372 nodep->dtypeSetUInt32(); // Says the spec
1373 AstNodeDType* const expDTypep = nodep->findUInt32DType();
1374 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1375 userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1376 iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1377 iterateCheck(nodep, "RHS", nodep->rhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
1378 }
1379 }
1380 void visit(AstUnbounded* nodep) override {
1381 nodep->dtypeSetSigned32(); // Used in int context
1382 if (VN_IS(nodep->backp(), IsUnbounded)) return; // Ok, leave
1383 if (VN_IS(nodep->backp(), BracketArrayDType)) return; // Ok, leave
1384 if (const auto* const varp = VN_CAST(nodep->backp(), Var)) {
1385 if (varp->isParam()) return; // Ok, leave
1386 }
1387 AstNode* backp = nodep->backp();
1388 if (VN_IS(backp, Sub)) backp = backp->backp();
1389 if (const auto* const selp = VN_CAST(backp, SelExtract)) {
1390 if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return;
1391 }
1392 if (const auto* const selp = VN_CAST(backp, SelBit)) {
1393 if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return;
1394 }
1395 // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel
1396 nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context.");
1397 }
1398 void visit(AstInferredDisable* nodep) override {
1399 if (m_vup->prelim()) nodep->dtypeSetBit();
1400 }
1401 void visit(AstIsUnbounded* nodep) override {
1402 if (m_vup->prelim()) {
1403 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1404 nodep->dtypeSetBit();
1405 }
1406 }
1407 void visit(AstUCFunc* nodep) override {
1408 // Give it the size the user wants.
1409 if (m_vup && m_vup->prelim()) {
1410 nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // We don't care
1411 // All arguments seek their natural sizes
1412 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1413 }
1414 if (m_vup->final()) {
1415 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1416 nodep->dtypep(expDTypep); // Assume user knows the rules; go with the flow
1417 if (nodep->width() > 64) {
1418 nodep->v3warn(E_UNSUPPORTED, "Unsupported: $c can't generate wider than 64 bits");
1419 }
1420 }
1421 }
1422 void visit(AstCLog2* nodep) override {
1423 if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1424 }
1425 void visit(AstPow* nodep) override {
1426 // Pow is special, output sign only depends on LHS sign, but
1427 // function result depends on both signs
1428 // RHS is self-determined (IEEE)
1429 // Real if either side is real (as with AstAdd)
1430 if (m_vup->prelim()) {
1431 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1432 userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
1433 if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
1434 spliceCvtD(nodep->lhsp());
1435 spliceCvtD(nodep->rhsp());
1436 VL_DO_DANGLING(replaceWithDVersion(nodep), nodep);
1437 return;
1438 }
1439
1440 checkCvtUS(nodep->lhsp());
1441 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1442 nodep->dtypeFrom(nodep->lhsp());
1443 }
1444
1445 if (m_vup->final()) {
1446 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
1447 nodep->dtypep(expDTypep);
1448 // rhs already finalized in iterate_shift_prelim
1449 iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, nodep->dtypep(), EXTEND_EXP);
1450 AstNode* newp = nullptr; // No change
1451 if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1452 newp = new AstPowSS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1453 nodep->rhsp()->unlinkFrBack()};
1454 } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) {
1455 newp = new AstPowSU{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1456 nodep->rhsp()->unlinkFrBack()};
1457 } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
1458 newp = new AstPowUS{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
1459 nodep->rhsp()->unlinkFrBack()};
1460 }
1461 if (newp) {
1462 newp->dtypeFrom(nodep);
1463 UINFO(9, "powOld " << nodep << endl);
1464 UINFO(9, "powNew " << newp << endl);
1465 nodep->replaceWith(newp);
1466 VL_DO_DANGLING(pushDeletep(nodep), nodep);
1467 }
1468 }
1469 }
1470 void visit(AstPowSU* nodep) override {
1471 // POWSU/SS/US only created here, dtype already determined, so
1472 // nothing to do in this function
1473 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1474 userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1475 }
1476 void visit(AstPowSS* nodep) override {
1477 // POWSU/SS/US only created here, dtype already determined, so
1478 // nothing to do in this function
1479 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1480 userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1481 }
1482 void visit(AstPowUS* nodep) override {
1483 // POWSU/SS/US only created here, dtype already determined, so
1484 // nothing to do in this function
1485 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1486 userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
1487 }
1488 void visit(AstCountBits* nodep) override {
1489 if (m_vup->prelim()) {
1490 iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1491 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
1492 iterateCheckSizedSelf(nodep, "THS", nodep->thsp(), SELF, BOTH);
1493 iterateCheckSizedSelf(nodep, "FHS", nodep->fhsp(), SELF, BOTH);
1494 // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'.
1495 const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1;
1496 nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED);
1497 }
1498 }
1499 void visit(AstCountOnes* nodep) override {
1500 if (m_vup->prelim()) {
1501 iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
1502 // For widthMin, if a 32 bit number, we need a 6 bit number as we need to return '32'.
1503 const int widthMin = V3Number::log2b(nodep->lhsp()->width()) + 1;
1504 nodep->dtypeSetLogicUnsized(32, widthMin, VSigning::SIGNED);
1505 }
1506 }
1507 void visit(AstCvtPackString* nodep) override {
1508 if (nodep->didWidthAndSet()) return;
1509 // Opaque returns, so arbitrary
1510 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1511 // Type set in constructor
1512 }
1513 void visit(AstTimeImport* nodep) override {
1514 // LHS is a real number in seconds
1515 // Need to round to time units and precision
1516 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
1517 const AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
1518 UASSERT_OBJ(constp && constp->isDouble(), nodep, "Times should be doubles");
1519 UASSERT_OBJ(!nodep->timeunit().isNone(), nodep, "$time import no units");
1520 const double timePrescale = constp->num().toDouble();
1521 UASSERT_OBJ(!v3Global.rootp()->timeprecision().isNone(), nodep, "Never set precision?");
1522 const double time = timePrescale / nodep->timeunit().multiplier();
1523 // IEEE claims you should round to time precision here, but no simulator seems to do this
1524 AstConst* const newp = new AstConst{nodep->fileline(), AstConst::RealDouble{}, time};
1525 nodep->replaceWith(newp);
1526 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1527 }
1528 void visit(AstEventControl* nodep) override {
1529 if (VN_IS(m_ftaskp, Func)) {
1530 nodep->v3error("Event controls are not legal in functions. Suggest use a task "
1531 "(IEEE 1800-2023 13.4.4)");
1532 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
1533 return;
1534 }
1535 if (nodep->fileline()->timingOn()) {
1536 if (v3Global.opt.timing().isSetTrue()) {
1537 iterateChildren(nodep);
1538 return;
1539 } else if (v3Global.opt.timing().isSetFalse()) {
1540 nodep->v3warn(E_NOTIMING,
1541 "Event control statement in this location requires --timing\n"
1542 << nodep->warnMore()
1543 << "... With --no-timing, suggest have one event control "
1544 << "statement per procedure, at the top of the procedure");
1545 } else {
1546 nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
1547 "event controls should be handled");
1548 }
1549 }
1550 if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBack());
1551 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
1552 }
1553 3568 void visit(AstAttrOf* nodep) override {
1554
1/2
✓ Branch 1 taken 3568 times.
✗ Branch 2 not taken.
3568 VL_RESTORER(m_attrp);
1555 3568 m_attrp = nodep;
1556
3/6
✓ Branch 1 taken 3568 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3568 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3568 times.
3568 userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
1557
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3568 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3568 if (nodep->dimp()) userIterateAndNext(nodep->dimp(), WidthVP{SELF, BOTH}.p());
1558 // Don't iterate children, don't want to lose VarRef.
1559
1/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3568 times.
3568 switch (nodep->attrType()) {
1560 case VAttrType::VAR_BASE:
1561 // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf
1562 break;
1563 case VAttrType::DIM_DIMENSIONS:
1564 case VAttrType::DIM_UNPK_DIMENSIONS: {
1565 UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
1566 const std::pair<uint32_t, uint32_t> dim = nodep->fromp()->dtypep()->dimensions(true);
1567 const int val
1568 = (nodep->attrType() == VAttrType::DIM_UNPK_DIMENSIONS ? dim.second
1569 : (dim.first + dim.second));
1570 nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32{}, val));
1571 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1572 break;
1573 }
1574 case VAttrType::DIM_BITS:
1575 case VAttrType::DIM_HIGH:
1576 case VAttrType::DIM_INCREMENT:
1577 case VAttrType::DIM_LEFT:
1578 case VAttrType::DIM_LOW:
1579 case VAttrType::DIM_RIGHT:
1580 case VAttrType::DIM_SIZE: {
1581 UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
1582 AstNodeDType* const dtypep = nodep->fromp()->dtypep();
1583 if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)) {
1584 switch (nodep->attrType()) {
1585 case VAttrType::DIM_SIZE: {
1586 AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
1587 AstNode* const newp = new AstCMethodHard{nodep->fileline(), fromp, "size"};
1588 newp->dtypeSetSigned32();
1589 newp->didWidth(true);
1590 newp->protect(false);
1591 nodep->replaceWith(newp);
1592 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1593 break;
1594 }
1595 case VAttrType::DIM_LEFT:
1596 case VAttrType::DIM_LOW: {
1597 AstNode* const newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, 0);
1598 nodep->replaceWith(newp);
1599 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1600 break;
1601 }
1602 case VAttrType::DIM_RIGHT:
1603 case VAttrType::DIM_HIGH: {
1604 AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
1605 AstNodeExpr* const sizep
1606 = new AstCMethodHard{nodep->fileline(), fromp, "size"};
1607 sizep->dtypeSetSigned32();
1608 sizep->didWidth(true);
1609 sizep->protect(false);
1610 AstNode* const newp
1611 = new AstSub{nodep->fileline(), sizep,
1612 new AstConst(nodep->fileline(), AstConst::Signed32{}, 1)};
1613 nodep->replaceWith(newp);
1614 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1615 break;
1616 }
1617 case VAttrType::DIM_INCREMENT: {
1618 AstNodeExpr* const newp
1619 = new AstConst(nodep->fileline(), AstConst::Signed32{}, -1);
1620 nodep->replaceWith(newp);
1621 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1622 break;
1623 }
1624 case VAttrType::DIM_BITS: {
1625 if (VN_IS(dtypep, DynArrayDType)) {
1626 nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for dynamic array");
1627 } else {
1628 nodep->v3warn(E_UNSUPPORTED, "Unsupported: $bits for queue");
1629 }
1630 break;
1631 }
1632 default: nodep->v3fatalSrc("Unhandled attribute type");
1633 }
1634 } else {
1635 const std::pair<uint32_t, uint32_t> dimpair = dtypep->skipRefp()->dimensions(true);
1636 const uint32_t msbdim = dimpair.first + dimpair.second;
1637 if (!nodep->dimp() || msbdim < 1) {
1638 if (VN_IS(dtypep, BasicDType) && dtypep->basicp()->isString()) {
1639 // IEEE undocumented but $bits(string) must give length(string) * 8
1640 AstNodeExpr* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), NodeExpr);
1641 AstNode* const newp = new AstShiftL{
1642 nodep->fileline(), new AstLenN{nodep->fileline(), fromp},
1643 new AstConst{nodep->fileline(), 3}, // * 8
1644 32};
1645 nodep->replaceWith(newp);
1646 VL_DO_DANGLING(pushDeletep(nodep), nodep);
1647 } else {
1648 const int dim = 1;
1649 AstConst* const newp
1650 = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
1651 nodep->replaceWith(newp);
1652 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1653 }
1654 } else if (VN_IS(nodep->dimp(), Const)) {
1655 const int dim = VN_AS(nodep->dimp(), Const)->toSInt();
1656 AstConst* const newp
1657 = dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
1658 nodep->replaceWith(newp);
1659 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1660 } else { // Need a runtime lookup table. Yuk.
1661 UASSERT_OBJ(nodep->fromp() && dtypep, nodep, "Unsized expression");
1662 AstVar* const varp = dimensionVarp(dtypep, nodep->attrType(), msbdim);
1663 AstNodeExpr* const dimp = nodep->dimp()->unlinkFrBack();
1664 AstNodeExpr* const newp
1665 = new AstArraySel{nodep->fileline(), newVarRefDollarUnit(varp), dimp};
1666 nodep->replaceWith(newp);
1667 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1668 }
1669 }
1670 break;
1671 }
1672 case VAttrType::TYPENAME: {
1673 UASSERT_OBJ(nodep->fromp(), nodep, "Unprovided expression");
1674 const string result = nodep->fromp()->dtypep()->prettyDTypeName(true);
1675 UINFO(9, "typename '" << result << "' from " << nodep->fromp()->dtypep() << "\n");
1676 AstNode* const newp = new AstConst{nodep->fileline(), AstConst::String{}, result};
1677 nodep->replaceWith(newp);
1678 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1679 break;
1680 }
1681 case VAttrType::TYPEID:
1682 // Soon to be handled in AstEqT
1683 nodep->dtypeSetSigned32();
1684 break;
1685 default: {
1686 // Everything else resolved earlier
1687 nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // Approximation, unsized 32
1688 UINFO(1, "Missing ATTR type case node: " << nodep << endl);
1689 nodep->v3fatalSrc("Missing ATTR type case");
1690 break;
1691 }
1692 }
1693 3568 }
1694 void visit(AstPull* nodep) override {
1695 // May have select underneath, let seek natural size
1696 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1697 }
1698 void visit(AstText* nodep) override {
1699 // Only used in CStmts which don't care....
1700 }
1701
1702 // DTYPES
1703 void visit(AstNodeArrayDType* nodep) override {
1704 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1705
1706 if (nodep->subDTypep() == nodep->basicp()) { // Innermost dimension
1707 AstBasicDType* const basicp = nodep->basicp();
1708 // If basic dtype is LOGIC_IMPLICIT, it is actually 1 bit LOGIC
1709 if (basicp->implicit()) {
1710 UASSERT_OBJ(basicp->width() <= 1, basicp,
1711 "must be 1 bit but actually " << basicp->width() << " bits");
1712 AstBasicDType* const newp = new AstBasicDType{
1713 basicp->fileline(), VBasicDTypeKwd::LOGIC, basicp->numeric()};
1714 newp->widthForce(1, 1);
1715 basicp->replaceWith(newp);
1716 VL_DO_DANGLING(pushDeletep(basicp), basicp);
1717 }
1718 }
1719 // Iterate into subDTypep() to resolve that type and update pointer.
1720 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1721 // Cleanup array size
1722 userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
1723 nodep->dtypep(nodep); // The array itself, not subDtype
1724 if (auto* const adtypep = VN_CAST(nodep, UnpackArrayDType)) {
1725 // Historically array elements have width of the ref type not the full array
1726 nodep->widthFromSub(nodep->subDTypep());
1727 if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true);
1728 } else {
1729 const int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst();
1730 nodep->widthForce(width, width);
1731 }
1732 UINFO(4, "dtWidthed " << nodep << endl);
1733 }
1734 void visit(AstAssocArrayDType* nodep) override {
1735 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1736 // Iterate into subDTypep() to resolve that type and update pointer.
1737 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1738 nodep->keyDTypep(iterateEditMoveDTypep(nodep, nodep->keyDTypep()));
1739 nodep->dtypep(nodep); // The array itself, not subDtype
1740 UINFO(4, "dtWidthed " << nodep << endl);
1741 }
1742 void visit(AstBracketArrayDType* nodep) override {
1743 // Type inserted only because parser didn't know elementsp() type
1744 // Resolve elementsp's type
1745 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1746 // We must edit when dtype still under normal nodes and before type table
1747 // See notes in iterateEditMoveDTypep
1748 AstNodeDType* const childp = nodep->childDTypep();
1749 childp->unlinkFrBack();
1750 AstNode* const elementsp = nodep->elementsp()->unlinkFrBack();
1751 AstNode* newp;
1752 if (VN_IS(elementsp, Unbounded)) {
1753 newp = new AstQueueDType{nodep->fileline(), VFlagChildDType{}, childp, nullptr};
1754 VL_DO_DANGLING(elementsp->deleteTree(), elementsp);
1755 } else if (AstNodeDType* const keyp = VN_CAST(elementsp, NodeDType)) {
1756 newp = new AstAssocArrayDType{nodep->fileline(), VFlagChildDType{}, childp, keyp};
1757 } else {
1758 // The subtract in the range may confuse users; as the array
1759 // size is self determined there's no reason to warn about widths
1760 FileLine* const elementsNewFl = elementsp->fileline();
1761 elementsNewFl->warnOff(V3ErrorCode::WIDTHEXPAND, true);
1762 // Must be expression that is constant, but we'll determine that later
1763 newp = new AstUnpackArrayDType{
1764 nodep->fileline(), VFlagChildDType{}, childp,
1765 new AstRange{nodep->fileline(), new AstConst(elementsp->fileline(), 0),
1766 new AstSub{elementsNewFl, VN_AS(elementsp, NodeExpr),
1767 new AstConst(elementsNewFl, 1)}}};
1768 }
1769 nodep->replaceWith(newp);
1770 VL_DO_DANGLING(nodep->deleteTree(), nodep);
1771 // Normally parent's iteration would cover this, but we might have entered by a specific
1772 // visit
1773 VL_DO_DANGLING(userIterate(newp, nullptr), newp);
1774 }
1775 void visit(AstConstraintRefDType* nodep) override {
1776 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1777 nodep->dtypep(nodep);
1778 }
1779 void visit(AstDynArrayDType* nodep) override {
1780 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1781 // Iterate into subDTypep() to resolve that type and update pointer.
1782 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1783 nodep->dtypep(nodep); // The array itself, not subDtype
1784 UINFO(4, "dtWidthed " << nodep << endl);
1785 }
1786 void visit(AstQueueDType* nodep) override {
1787 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1788 // Iterate into subDTypep() to resolve that type and update pointer.
1789 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1790 nodep->dtypep(nodep); // The array itself, not subDtype
1791 userIterateAndNext(nodep->boundp(), WidthVP{SELF, BOTH}.p());
1792 if (VN_IS(nodep->boundp(), Unbounded)) {
1793 nodep->boundp()->unlinkFrBack()->deleteTree(); // nullptr will represent unbounded
1794 }
1795 UINFO(4, "dtWidthed " << nodep << endl);
1796 }
1797
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 void visit(AstVoidDType* nodep) override {
1798 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1799 nodep->dtypep(nodep);
1800
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
80 UINFO(4, "dtWidthed " << nodep << endl);
1801 }
1802 void visit(AstUnsizedArrayDType* nodep) override {
1803 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1804 // Iterate into subDTypep() to resolve that type and update pointer.
1805 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1806 // Cleanup array size
1807 nodep->dtypep(nodep); // The array itself, not subDtype
1808 UINFO(4, "dtWidthed " << nodep << endl);
1809 }
1810 void visit(AstWildcardArrayDType* nodep) override {
1811 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1812 // Iterate into subDTypep() to resolve that type and update pointer.
1813 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1814 // Cleanup array size
1815 nodep->dtypep(nodep); // The array itself, not subDtype
1816 UINFO(4, "dtWidthed " << nodep << endl);
1817 }
1818
2/2
✓ Branch 0 taken 7478 times.
✓ Branch 1 taken 12843 times.
20321 void visit(AstBasicDType* nodep) override {
1819 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1820
2/2
✓ Branch 0 taken 3694 times.
✓ Branch 1 taken 3784 times.
7478 if (nodep->generic()) return; // Already perfect
1821
2/2
✓ Branch 0 taken 1680 times.
✓ Branch 1 taken 2014 times.
3694 if (nodep->rangep()) {
1822 1680 userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
1823 // Because this DType has a unique child range, we know it's not
1824 // pointed at by other nodes unless they are referencing this type.
1825 // Furthermore the width() calculation would return identical
1826 // values. Therefore we can directly replace the width
1827 1680 nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst());
1828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2014 times.
2014 } else if (nodep->isRanged()) {
1829 nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements());
1830
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2014 times.
2014 } else if (nodep->implicit()) {
1831 // Parameters may notice implicitness and change to different dtype
1832 nodep->widthForce(1, 1);
1833 }
1834 // else width in node is correct; it was set based on keyword().width()
1835 // at construction time. Ditto signed, so "unsigned byte" etc works right.
1836 3694 nodep->cvtRangeConst();
1837 // TODO: If BasicDType now looks like a generic type, we can convert to a real generic
1838 // dtype Instead for now doing this in V3WidthCommit
1839
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3694 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
3694 UINFO(4, "dtWidthed " << nodep << endl);
1840 }
1841 void visit(AstConstDType* nodep) override {
1842 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1843 // Iterate into subDTypep() to resolve that type and update pointer.
1844 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1845 userIterateChildren(nodep, nullptr);
1846 nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType
1847 nodep->widthFromSub(nodep->subDTypep());
1848 UINFO(4, "dtWidthed " << nodep << endl);
1849 }
1850 void visit(AstRefDType* nodep) override {
1851 if (nodep->doingWidth()) { // Early exit if have circular parameter definition
1852 nodep->v3error("Typedef's type is circular: " << nodep->prettyName());
1853 nodep->dtypeSetBit();
1854 nodep->doingWidth(false);
1855 return;
1856 }
1857 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1858 nodep->doingWidth(true);
1859 if (nodep->typeofp()) { // type(typeofp_expression)
1860 if (AstNodeDType* typeofDtp = VN_CAST(nodep->typeofp(), NodeDType)) {
1861 // It's directly a type, e.g. "type(int)"
1862 typeofDtp = iterateEditMoveDTypep(nodep, typeofDtp); // Changes typeofp
1863 nodep->refDTypep(typeofDtp);
1864 } else {
1865 // Type comes from expression's type, e.g. "type(variable)"
1866 userIterateAndNext(nodep->typeofp(), WidthVP{SELF, BOTH}.p());
1867 AstNode* const typeofp = nodep->typeofp();
1868 nodep->refDTypep(typeofp->dtypep());
1869 VL_DO_DANGLING(typeofp->unlinkFrBack()->deleteTree(), typeofp);
1870 }
1871 // We had to use AstRefDType for this construct as pointers to this type
1872 // in type table are still correct (which they wouldn't be if we replaced the node)
1873 }
1874 userIterateChildren(nodep, nullptr);
1875 if (nodep->subDTypep()) {
1876 // Normally iterateEditMoveDTypep iterate would work, but the refs are under
1877 // the TypeDef which will upset iterateEditMoveDTypep as it can't find it under
1878 // this node's childDTypep
1879 userIterate(nodep->subDTypep(), nullptr);
1880 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1881 // Widths are resolved, but special iterate to check for recursion
1882 userIterate(nodep->subDTypep(), nullptr);
1883 }
1884 // Effectively nodep->dtypeFrom(nodep->dtypeSkipRefp());
1885 // But might be recursive, so instead manually recurse into the referenced type
1886 UASSERT_OBJ(nodep->subDTypep(), nodep, "Unlinked");
1887 nodep->dtypeFrom(nodep->subDTypep());
1888 nodep->widthFromSub(nodep->subDTypep());
1889 UINFO(4, "dtWidthed " << nodep << endl);
1890 // No nodep->typedefp(nullptr) for now; V3WidthCommit needs to check accesses
1891 nodep->doingWidth(false);
1892 }
1893 void visit(AstTypedef* nodep) override {
1894 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1895 if (auto* const refp = checkRefToTypedefRecurse(nodep, nodep)) {
1896 nodep->v3error("Typedef has self-reference: " << nodep->prettyNameQ() << '\n'
1897 << nodep->warnContextPrimary() << '\n'
1898 << refp->warnOther()
1899 << "... Location of reference\n"
1900 << refp->warnContextSecondary());
1901 // May cause internal error but avoids infinite loop on dump
1902 refp->typedefp(nullptr);
1903 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
1904 return;
1905 }
1906 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1907 userIterateChildren(nodep, nullptr);
1908 }
1909 void visit(AstParamTypeDType* nodep) override {
1910 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
1911 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1912 userIterateChildren(nodep, nullptr);
1913 nodep->widthFromSub(nodep->subDTypep());
1914 }
1915 void visit(AstCastDynamic* nodep) override {
1916 nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
1917 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
1918 AstNodeDType* const toDtp = nodep->top()->dtypep()->skipRefToEnump();
1919 AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
1920 FileLine* const fl = nodep->fileline();
1921 const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp());
1922 AstNode* newp;
1923 if (castable == VCastable::DYNAMIC_CLASS) {
1924 // Keep in place, will compute at runtime
1925 return;
1926 } else if (castable == VCastable::ENUM_EXPLICIT || castable == VCastable::ENUM_IMPLICIT) {
1927 // TODO is from is a constant we could simplify, though normal constant
1928 // elimination should do much the same
1929 // Form: "( ((v > size) ? false : enum_valid[v[N:0]])
1930 // ? ExprStmt(ExprAssign(out, Cast(v, type)), 1) : 0)"
1931 AstEnumDType* const enumDtp = VN_AS(toDtp, EnumDType);
1932 UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type");
1933 AstNodeExpr* const testp
1934 = enumTestValid(nodep->fromp()->cloneTreePure(false), enumDtp);
1935 FileLine* const fl_novalue = new FileLine{fl};
1936 fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true);
1937 newp = new AstCond{
1938 fl, testp,
1939 new AstExprStmt{fl,
1940 new AstAssign{fl_novalue, nodep->top()->unlinkFrBack(),
1941 nodep->fromp()->unlinkFrBack()},
1942 new AstConst{fl, AstConst::Signed32{}, 1}},
1943 new AstConst{fl, AstConst::Signed32{}, 0}};
1944 } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
1945 nodep->v3warn(CASTCONST, "$cast will always return one as "
1946 << toDtp->prettyDTypeNameQ()
1947 << " is always castable from "
1948 << fromDtp->prettyDTypeNameQ() << '\n'
1949 << nodep->warnMore() << "... Suggest static cast");
1950 newp = new AstExprStmt{
1951 fl,
1952 new AstAssign{fl, nodep->top()->unlinkFrBack(),
1953 new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}},
1954 new AstConst{fl, AstConst::Signed32{}, 1}};
1955 } else if (castable == VCastable::INCOMPATIBLE) {
1956 newp = new AstConst{fl, 0};
1957 nodep->v3warn(CASTCONST, "$cast will always return zero as "
1958 << toDtp->prettyDTypeNameQ() << " is not castable from "
1959 << fromDtp->prettyDTypeNameQ());
1960 } else {
1961 newp = new AstConst{fl, 0};
1962 nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast to "
1963 << toDtp->prettyDTypeNameQ() << " from "
1964 << fromDtp->prettyDTypeNameQ() << '\n'
1965 << nodep->warnMore()
1966 << "... Suggest try static cast");
1967 }
1968 newp->dtypeFrom(nodep);
1969 nodep->replaceWith(newp);
1970 VL_DO_DANGLING(pushDeletep(nodep), nodep);
1971 userIterate(newp, m_vup);
1972 }
1973 void visit(AstCastParse* nodep) override {
1974 // nodep->dtp could be data type, or a primary_constant
1975 // Don't iterate lhsp, will deal with that once convert the type
1976 V3Const::constifyParamsEdit(nodep->dtp()); // itemp may change
1977 if (AstConst* const constp = VN_CAST(nodep->dtp(), Const)) {
1978 constp->unlinkFrBack();
1979 AstNode* const newp
1980 = new AstCastSize{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), constp};
1981 nodep->replaceWith(newp);
1982 VL_DO_DANGLING(pushDeletep(nodep), nodep);
1983 userIterate(newp, m_vup);
1984 } else {
1985 nodep->v3warn(E_UNSUPPORTED,
1986 "Unsupported: Cast to " << nodep->dtp()->prettyTypeName());
1987 nodep->replaceWith(nodep->lhsp()->unlinkFrBack());
1988 VL_DO_DANGLING(pushDeletep(nodep), nodep);
1989 }
1990 }
1991 void visit(AstCast* nodep) override {
1992 if (nodep->didWidth()) return;
1993 UINFO(9, "CAST " << nodep << endl);
1994 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
1995 if (m_vup->prelim()) {
1996 if (debug() >= 9) nodep->dumpTree("- CastPre: ");
1997 // if (debug()) nodep->backp()->dumpTree("- CastPreUpUp: ");
1998 userIterateAndNext(nodep->fromp(), WidthVP{SELF, PRELIM}.p());
1999 AstNodeDType* const toDtp = nodep->dtypep()->skipRefToEnump();
2000 AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2001 const auto castable = AstNode::computeCastable(toDtp, fromDtp, nodep->fromp());
2002 bool bad = false;
2003 if (castable == VCastable::UNSUPPORTED) {
2004 nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to "
2005 << toDtp->prettyDTypeNameQ() << " from "
2006 << fromDtp->prettyDTypeNameQ());
2007 bad = true;
2008 } else if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE
2009 || castable == VCastable::ENUM_IMPLICIT
2010 || castable == VCastable::ENUM_EXPLICIT) {
2011 ; // Continue
2012 } else if (castable == VCastable::DYNAMIC_CLASS) {
2013 nodep->v3error("Dynamic, not static cast, required to cast "
2014 << toDtp->prettyDTypeNameQ() << " from "
2015 << fromDtp->prettyDTypeNameQ() << '\n'
2016 << nodep->warnMore() << "... Suggest dynamic $cast");
2017 bad = true;
2018 } else if (castable == VCastable::INCOMPATIBLE) {
2019 nodep->v3error("Incompatible types to static cast to "
2020 << toDtp->prettyDTypeNameQ() << " from "
2021 << fromDtp->prettyDTypeNameQ() << '\n');
2022 bad = true;
2023 } else {
2024 nodep->v3fatalSrc("bad casting case");
2025 }
2026 // For now, replace it ASAP, so widthing can propagate easily
2027 // The cast may change signing, but we don't know the sign yet. Make it so.
2028 // Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
2029 AstNodeExpr* newp = nullptr;
2030 if (bad) {
2031 } else if (const AstBasicDType* const basicp = toDtp->basicp()) {
2032 if (!basicp->isString() && fromDtp->isString()) {
2033 newp = new AstNToI{nodep->fileline(), nodep->fromp()->unlinkFrBack(), toDtp};
2034 } else if (!basicp->isDouble() && !fromDtp->isDouble()) {
2035 AstNodeDType* const origDTypep = nodep->dtypep();
2036 if (!VN_IS(fromDtp, StreamDType)) {
2037 const int width = toDtp->width();
2038 castSized(nodep, nodep->fromp(), width);
2039 }
2040 nodep->dtypeFrom(origDTypep); // If was enum, need dtype to preserve as enum
2041 // Note castSized might modify nodep->fromp()
2042 } else {
2043 iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, fromDtp, EXTEND_EXP,
2044 false);
2045 }
2046 if (newp) {
2047 } else if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
2048 if (nodep->fromp()->isSigned()) {
2049 newp = new AstISToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2050 } else {
2051 newp = new AstIToRD{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2052 }
2053 } else if (!basicp->isDouble() && nodep->fromp()->isDouble()) {
2054 newp = new AstRToIRoundS{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2055 newp->dtypeChgSigned(basicp->isSigned());
2056 } else if (basicp->isSigned() && !nodep->fromp()->isSigned()) {
2057 newp = new AstSigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2058 } else if (!basicp->isSigned() && nodep->fromp()->isSigned()) {
2059 newp = new AstUnsigned{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
2060 } else {
2061 // Can just remove cast, but need extend placeholder
2062 // so we can avoid warning message
2063 }
2064 } else if (VN_IS(toDtp, ClassRefDType)) {
2065 // Can just remove cast
2066 } else {
2067 nodep->v3fatalSrc("Unimplemented: Casting non-simple data type "
2068 << toDtp->prettyDTypeNameQ());
2069 }
2070 if (!newp) newp = nodep->fromp()->unlinkFrBack();
2071 nodep->fromp(newp);
2072 if (debug() >= 9) nodep->dumpTree("- CastOut: ");
2073 // if (debug()) nodep->backp()->dumpTree("- CastOutUpUp: ");
2074 }
2075 if (m_vup->final()) {
2076 if (debug() >= 9) nodep->dumpTree(cout, "- CastFPit: ");
2077 iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(),
2078 EXTEND_EXP, false);
2079 if (debug() >= 9) nodep->dumpTree("- CastFin: ");
2080 AstNodeExpr* const underp = nodep->fromp()->unlinkFrBack();
2081 underp->dtypeFrom(nodep);
2082 underp->didWidth(true);
2083 AstNodeExpr* const newp = new AstCastWrap{nodep->fileline(), underp};
2084 newp->didWidth(true);
2085 if (debug() >= 9) newp->dumpTree("- CastRep: ");
2086 nodep->replaceWith(newp);
2087 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2088 }
2089 }
2090 void visit(AstCastSize* nodep) override {
2091 // IEEE: Signedness of result is same as self-determined signedness
2092 // However, the result is same as BITSEL, so we do not sign extend the LHS
2093 // if (debug()) nodep->dumpTree("- CastSizePre: ");
2094 if (m_vup->prelim()) {
2095 int width = nodep->rhsp()->toSInt();
2096 if (width < 1) {
2097 nodep->v3error("Size-changing cast to zero or negative size");
2098 width = 1;
2099 }
2100 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
2101 castSized(nodep, nodep->lhsp(), width); // lhsp may change
2102 }
2103 if (m_vup->final()) {
2104 // CastSize not needed once sizes determined
2105 AstNode* const underp = nodep->lhsp()->unlinkFrBack();
2106 underp->dtypeFrom(nodep);
2107 nodep->replaceWith(underp);
2108 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2109 }
2110 // if (debug()) nodep->dumpTree("- CastSizeOut: ");
2111 }
2112 void visit(AstCastWrap* nodep) override {
2113 // Inserted by V3Width only so we know has been resolved
2114 userIterateAndNext(nodep->lhsp(), WidthVP{nodep->dtypep(), BOTH}.p());
2115 }
2116 void castSized(AstNode* nodep, AstNode* underp, int width) {
2117 const AstBasicDType* underDtp = VN_CAST(underp->dtypep(), BasicDType);
2118 if (!underDtp) underDtp = underp->dtypep()->basicp();
2119 if (!underDtp) {
2120 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Size-changing cast on non-basic data type");
2121 underDtp = VN_AS(nodep->findBitDType(), BasicDType);
2122 }
2123 UASSERT_OBJ(underp == nodep->op1p(), nodep, "Assuming op1 is cast value");
2124 // A cast propagates its size to the lower expression and is included in the maximum
2125 // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math.
2126 // However the output width is exactly that requested.
2127 // So two steps, first do the calculation's width (max of the two widths)
2128 {
2129 const int calcWidth = std::max(width, underDtp->width());
2130 AstNodeDType* const calcDtp
2131 = (underDtp->isFourstate()
2132 ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric())
2133 : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric()));
2134 nodep->dtypep(calcDtp);
2135 // We ignore warnings as that is sort of the point of a cast
2136 iterateCheck(nodep, "Cast expr", underp, CONTEXT_DET, FINAL, calcDtp, EXTEND_EXP,
2137 false);
2138 VL_DANGLING(underp);
2139 underp = nodep->op1p(); // Above asserts that op1 was underp pre-relink
2140 }
2141 // if (debug()) nodep->dumpTree("- CastSizeClc: ");
2142 // Next step, make the proper output width
2143 {
2144 AstNodeDType* const outDtp
2145 = (underDtp->isFourstate()
2146 ? nodep->findLogicDType(width, width, underDtp->numeric())
2147 : nodep->findBitDType(width, width, underDtp->numeric()));
2148 nodep->dtypep(outDtp);
2149 // We ignore warnings as that is sort of the point of a cast
2150 widthCheckSized(nodep, "Cast expr", VN_AS(underp, NodeExpr), outDtp, EXTEND_EXP,
2151 false);
2152 VL_DANGLING(underp);
2153 }
2154 }
2155 void visit(AstConstraintBefore* nodep) override {
2156 userIterateAndNext(nodep->lhssp(), WidthVP{SELF, BOTH}.p());
2157 userIterateAndNext(nodep->rhssp(), WidthVP{SELF, BOTH}.p());
2158 }
2159 void visit(AstConstraintExpr* nodep) override {
2160 userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p());
2161 }
2162 void visit(AstConstraintUnique* nodep) override {
2163 userIterateAndNext(nodep->rangesp(), WidthVP{SELF, BOTH}.p());
2164 }
2165 void visit(AstDistItem* nodep) override {
2166 userIterate(nodep->rangep(), m_vup);
2167 if (m_vup->prelim()) { // First stage evaluation
2168 userIterate(nodep->weightp(), WidthVP{SELF, BOTH}.p());
2169 }
2170 nodep->dtypep(nodep->rangep()->dtypep());
2171 }
2172
1/2
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
3694 void visit(AstVar* nodep) override {
2173 // if (debug()) nodep->dumpTree("- InitPre: ");
2174 // Must have deterministic constant width
2175 // We can't skip this step when width()!=0, as creating a AstVar
2176 // with non-constant range gets size 1, not size 0. So use didWidth().
2177
1/2
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
3694 if (nodep->didWidth()) return;
2178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
3694 if (nodep->doingWidth()) { // Early exit if have circular parameter definition
2179 UASSERT_OBJ(nodep->valuep(), nodep, "circular, but without value");
2180 nodep->v3error("Variable's initial value is circular: " << nodep->prettyNameQ());
2181 pushDeletep(nodep->valuep()->unlinkFrBack());
2182 nodep->valuep(new AstConst{nodep->fileline(), AstConst::BitTrue{}});
2183 nodep->dtypeFrom(nodep->valuep());
2184 nodep->didWidth(true);
2185 return;
2186 }
2187 nodep->doingWidth(true);
2188 // Make sure dtype is sized
2189
1/2
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
7388 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2190
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
3694 UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var");
2191
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3694 if (m_ftaskp && m_ftaskp->dpiImport()) {
2192 AstNodeDType* dtp = nodep->dtypep();
2193 AstNodeDType* np = nullptr;
2194 while (VN_IS(dtp->skipRefp(), DynArrayDType)
2195 || VN_IS(dtp->skipRefp(), UnpackArrayDType)) {
2196 if (const AstDynArrayDType* const unsizedp
2197 = VN_CAST(dtp->skipRefp(), DynArrayDType)) {
2198 if (!np) {
2199 UINFO(9, "Dynamic becomes unsized array (var itself) " << nodep << endl);
2200 } else {
2201 UINFO(9, "Dynamic becomes unsized array (subDType) " << dtp << endl);
2202 }
2203 AstUnsizedArrayDType* const newp
2204 = new AstUnsizedArrayDType{unsizedp->fileline(), unsizedp->subDTypep()};
2205 newp->dtypep(newp);
2206 if (!np) { // for Var itself
2207 nodep->dtypep(newp);
2208 } else { // for subDType
2209 np->virtRefDTypep(newp);
2210 }
2211 v3Global.rootp()->typeTablep()->addTypesp(newp);
2212 np = newp;
2213 } else {
2214 np = dtp->skipRefp();
2215 }
2216 dtp = np->virtRefDTypep();
2217 }
2218 }
2219 if (AstWildcardArrayDType* const wildp
2220 = VN_CAST(nodep->dtypeSkipRefp(), WildcardArrayDType)) {
2221 nodep->dtypep(wildp); // Skip RefDType like for other dynamic array types
2222 }
2223 if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) nodep->isConst(true);
2224 // Parameters if implicit untyped inherit from what they are assigned to
2225 const AstBasicDType* const bdtypep = VN_CAST(nodep->dtypep(), BasicDType);
2226 bool didchk = false;
2227
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3694 const bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit();
2228 if (implicitParam) {
2229 if (nodep->valuep()) {
2230 userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
2231 UINFO(9, "implicitParamPRELIMIV " << nodep->valuep() << endl);
2232 // Although nodep will get a different width for parameters
2233 // just below, we want the init numbers to retain their
2234 // width/minwidth until parameters are replaced.
2235 // This prevents width warnings at the location the parameter is substituted in
2236 if (nodep->valuep()->isDouble()) {
2237 nodep->dtypeSetDouble();
2238 VL_DANGLING(bdtypep);
2239 } else {
2240 int width = 0;
2241 const AstBasicDType* const valueBdtypep = nodep->valuep()->dtypep()->basicp();
2242 bool issigned = false;
2243 if (bdtypep->isNosign()) {
2244 if (valueBdtypep && valueBdtypep->isSigned()) issigned = true;
2245 } else {
2246 issigned = bdtypep->isSigned();
2247 }
2248 if (valueBdtypep->isString()) {
2249 // parameter X = "str", per IEEE is a number, not a string
2250 if (const auto* const constp = VN_CAST(nodep->valuep(), Const)) {
2251 if (constp->num().isString()) {
2252 width = constp->num().toString().length() * 8;
2253 }
2254 }
2255 if (width < 8) width = 8;
2256 } else if (nodep->valuep()->dtypep()->widthSized()) {
2257 width = nodep->valuep()->width();
2258 } else {
2259 if (nodep->valuep()->width() > 32) {
2260 nodep->valuep()->v3warn(
2261 WIDTH,
2262 "Assigning >32 bit to unranged parameter (defaults to 32 bits)");
2263 }
2264 width = 32;
2265 }
2266 // Can't just inherit valuep()->dtypep() as mwidth might not equal width
2267 if (width == 1) {
2268 // one bit parameter is same as "parameter [0] foo",
2269 // not "parameter logic foo" as you can extract
2270 // "foo[0]" from a parameter but not a wire
2271 nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
2272 VSigning::fromBool(issigned));
2273 nodep->dtypep(nodep->findLogicRangeDType(VNumRange{0, 0},
2274 nodep->valuep()->widthMin(),
2275 VSigning::fromBool(issigned)));
2276 } else {
2277 nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(),
2278 VSigning::fromBool(issigned));
2279 }
2280 didchk = true;
2281 }
2282 iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL,
2283 nodep->dtypep());
2284 UINFO(9, "implicitParamFromIV " << nodep->valuep() << endl);
2285 // UINFO below will print variable nodep
2286 } else {
2287 // Or, if nothing assigned, they're integral
2288 nodep->dtypeSetSigned32();
2289 VL_DANGLING(bdtypep);
2290 }
2291
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
✓ Branch 2 taken 3694 times.
✗ Branch 3 not taken.
3694 } else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1
2292 nodep->dtypeSetLogicSized(1, bdtypep->numeric());
2293 VL_DANGLING(bdtypep);
2294 }
2295
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3694 if (nodep->valuep() && !didchk) {
2296 // if (debug()) nodep->dumpTree("- final: ");
2297 // AstPattern requires assignments to pass datatype on PRELIM
2298 userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
2299 iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
2300 }
2301 3694 userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p());
2302
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 3694 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
3694 UINFO(4, "varWidthed " << nodep << endl);
2303 // if (debug()) nodep->dumpTree("- InitOut: ");
2304 nodep->didWidth(true);
2305 nodep->doingWidth(false);
2306 }
2307
2/2
✓ Branch 0 taken 23489 times.
✓ Branch 1 taken 26865 times.
50354 void visit(AstNodeVarRef* nodep) override {
2308
2/2
✓ Branch 0 taken 23489 times.
✓ Branch 1 taken 26865 times.
50354 if (nodep->didWidth()) return;
2309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23489 times.
23489 if (!nodep->varp()) {
2310 if (m_paramsOnly && VN_IS(nodep, VarXRef)) {
2311 checkConstantOrReplace(
2312 nodep, "Parameter-resolved constants must not use dotted references: "
2313 + nodep->prettyNameQ());
2314 VL_DANGLING(nodep);
2315 return;
2316 } else {
2317 nodep->v3fatalSrc("Unlinked varref");
2318 }
2319 }
2320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23489 times.
23489 if (!nodep->varp()->didWidth()) {
2321 // Var hasn't been widthed, so make it so.
2322 userIterate(nodep->varp(), nullptr);
2323 }
2324 // if (debug()>=9) { nodep->dumpTree("- VRin:: ");
2325 // nodep->varp()->dumpTree("- forvar: "); }
2326 // Note genvar's are also entered as integers
2327
1/2
✓ Branch 0 taken 23489 times.
✗ Branch 1 not taken.
23489 nodep->dtypeFrom(nodep->varp());
2328
2/2
✓ Branch 0 taken 3510 times.
✓ Branch 1 taken 71 times.
3581 if (VN_IS(nodep->backp(), NodeAssign) && nodep->access().isWriteOrRW()) { // On LHS
2329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3510 times.
3510 UASSERT_OBJ(nodep->dtypep(), nodep, "LHS var should be dtype completed");
2330 }
2331 // if (debug() >= 9) nodep->dumpTree("- VRout: ");
2332
3/4
✓ Branch 0 taken 4406 times.
✓ Branch 1 taken 19083 times.
✓ Branch 2 taken 4406 times.
✗ Branch 3 not taken.
23489 if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) {
2333 nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
2334
2/2
✓ Branch 0 taken 4406 times.
✓ Branch 1 taken 19083 times.
23489 } else if (!nodep->varp()->isForced() && nodep->access().isWriteOrRW()
2335
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4406 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4406 && nodep->varp()->isInput() && !nodep->varp()->isFuncLocal()
2336 && nodep->varp()->isReadOnly() && (!m_ftaskp || !m_ftaskp->isConstructor())
2337 && !VN_IS(m_procedurep, InitialAutomatic)
2338
1/2
✓ Branch 0 taken 23489 times.
✗ Branch 1 not taken.
23489 && !VN_IS(m_procedurep, InitialStatic)) {
2339 nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " << nodep->prettyNameQ());
2340
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4406 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4406 } else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly
2341 && (!m_ftaskp || !m_ftaskp->isConstructor())
2342 && !VN_IS(m_procedurep, InitialAutomatic)
2343
2/2
✓ Branch 0 taken 4406 times.
✓ Branch 1 taken 19083 times.
23489 && !VN_IS(m_procedurep, InitialStatic)) {
2344 // Too loose, but need to allow our generated first assignment
2345 // Move this to a property of the AstInitial block
2346 nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());
2347 }
2348 nodep->didWidth(true);
2349 }
2350
2351 void visit(AstEnumDType* nodep) override {
2352 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2353 UINFO(5, " ENUMDTYPE " << nodep << endl);
2354 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2355 nodep->dtypep(nodep);
2356 AstBasicDType* basicp = nodep->dtypep()->skipRefp()->basicp();
2357 if (!basicp || !basicp->keyword().isIntNumeric()) {
2358 nodep->v3error(
2359 "Enum type must be an integer atom or vector type (IEEE 1800-2023 6.19)");
2360 basicp = nodep->findSigned32DType()->basicp();
2361 nodep->refDTypep(basicp);
2362 }
2363 nodep->widthFromSub(nodep->subDTypep());
2364 // Assign widths
2365 userIterateAndNext(nodep->itemsp(), WidthVP{nodep->dtypep(), BOTH}.p());
2366 // Assign missing values
2367 V3Number num(nodep, nodep->width(), 0);
2368 const V3Number one{nodep, nodep->width(), 1};
2369 std::map<const V3Number, AstEnumItem*> inits;
2370 for (AstEnumItem* itemp = nodep->itemsp(); itemp;
2371 itemp = VN_AS(itemp->nextp(), EnumItem)) {
2372 if (itemp->valuep()) {
2373 if (debug() >= 9) {
2374 UINFO(0, "EnumInit " << itemp << endl);
2375 itemp->valuep()->dumpTree("- EnumInit: ");
2376 }
2377 V3Const::constifyParamsEdit(itemp->valuep()); // itemp may change
2378 if (!VN_IS(itemp->valuep(), Const)) {
2379 itemp->valuep()->v3error("Enum value isn't a constant");
2380 itemp->valuep()->unlinkFrBack()->deleteTree();
2381 continue;
2382 }
2383 // TODO IEEE says assigning sized number that is not same size as enum is illegal
2384 }
2385 if (!itemp->valuep()) {
2386 if (num.isEqZero() && itemp != nodep->itemsp()) {
2387 itemp->v3error("Enum value illegally wrapped around (IEEE 1800-2023 6.19)");
2388 }
2389 if (num.isFourState()) {
2390 itemp->v3error("Enum value that is unassigned cannot follow value with X/Zs "
2391 "(IEEE 1800-2023 6.19)");
2392 }
2393 itemp->valuep(new AstConst{itemp->fileline(), num});
2394 }
2395
2396 const AstConst* const constp = VN_AS(itemp->valuep(), Const);
2397 if (constp->num().isFourState() && basicp->basicp() && !basicp->isFourstate()) {
2398 itemp->v3error("Enum value with X/Zs cannot be assigned to non-fourstate type "
2399 "(IEEE 1800-2023 6.19)");
2400 }
2401 num.opAssign(constp->num());
2402 // Look for duplicates
2403 const auto pair = inits.emplace(num, itemp);
2404 if (!pair.second) { // IEEE says illegal
2405 const AstNode* const otherp = pair.first->second;
2406 itemp->v3error("Overlapping enumeration value: "
2407 << itemp->prettyNameQ() << '\n'
2408 << itemp->warnContextPrimary() << '\n'
2409 << otherp->warnOther() << "... Location of original declaration\n"
2410 << otherp->warnContextSecondary());
2411 }
2412 num.opAdd(one, constp->num());
2413 }
2414 }
2415 void visit(AstEnumItem* nodep) override {
2416 UINFO(5, " ENUMITEM " << nodep << endl);
2417 VL_RESTORER(m_enumItemp);
2418 m_enumItemp = nodep;
2419 AstNodeDType* const vdtypep = m_vup->dtypep();
2420 UASSERT_OBJ(vdtypep, nodep, "ENUMITEM not under ENUM");
2421 nodep->dtypep(vdtypep);
2422 if (nodep->valuep()) { // else the value will be assigned sequentially
2423 // Default type is int, but common to assign narrower values, so minwidth from value
2424 userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p());
2425 bool warnOn = true;
2426 if (const AstConst* const constp = VN_CAST(nodep->valuep(), Const)) {
2427 if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) {
2428 constp->v3error("Enum value exceeds width of enum type (IEEE 1800-2023 6.19)");
2429 warnOn = false; // Prevent normal WIDTHTRUNC
2430 }
2431 }
2432 // Minwidth does not come from value, as spec says set based on parent
2433 // and if we keep minwidth we'll consider it unsized which is incorrect
2434 iterateCheck(nodep, "Enum value", nodep->valuep(), CONTEXT_DET, FINAL, nodep->dtypep(),
2435 EXTEND_EXP, warnOn);
2436 // Always create a cast, to avoid later ENUMVALUE warnings
2437 nodep->valuep(new AstCast{nodep->valuep()->fileline(), nodep->valuep()->unlinkFrBack(),
2438 nodep->dtypep()});
2439 }
2440 }
2441 void visit(AstEnumItemRef* nodep) override {
2442 if (!nodep->itemp()->didWidth()) {
2443 // We need to do the whole enum en masse
2444 AstNode* enump = nodep->itemp();
2445 UASSERT_OBJ(enump, nodep, "EnumItemRef not linked");
2446 for (; enump; enump = enump->backp()) {
2447 if (VN_IS(enump, EnumDType)) break;
2448 }
2449 UASSERT_OBJ(enump, nodep, "EnumItemRef can't deref back to an Enum");
2450 VL_DO_DANGLING(userIterate(enump, m_vup), enump); // Parent enump maybe relinked
2451 }
2452 nodep->dtypeFrom(nodep->itemp());
2453 }
2454 void visit(AstConsAssoc* nodep) override {
2455 // Type computed when constructed here
2456 auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
2457 UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc upper parent data type");
2458 if (m_vup->prelim()) {
2459 nodep->dtypeFrom(vdtypep);
2460 if (nodep->defaultp()) {
2461 iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL,
2462 vdtypep->subDTypep(), EXTEND_EXP);
2463 }
2464 }
2465 }
2466 void visit(AstSetAssoc* nodep) override {
2467 // Type computed when constructed here
2468 auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), AssocArrayDType);
2469 UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc upper parent data type");
2470 if (m_vup->prelim()) {
2471 nodep->dtypeFrom(vdtypep);
2472 userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
2473 iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL, vdtypep->keyDTypep(),
2474 EXTEND_EXP);
2475 iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(),
2476 EXTEND_EXP);
2477 }
2478 }
2479 void visit(AstConsWildcard* nodep) override {
2480 // Type computed when constructed here
2481 auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
2482 UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type");
2483 if (m_vup->prelim()) {
2484 nodep->dtypeFrom(vdtypep);
2485 if (nodep->defaultp()) {
2486 iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT_DET, FINAL,
2487 vdtypep->subDTypep(), EXTEND_EXP);
2488 }
2489 }
2490 }
2491 void visit(AstSetWildcard* nodep) override {
2492 // Type computed when constructed here
2493 auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
2494 UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type");
2495 if (m_vup->prelim()) {
2496 nodep->dtypeFrom(vdtypep);
2497 userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
2498 iterateCheck(nodep, "key", nodep->keyp(), CONTEXT_DET, FINAL,
2499 vdtypep->findStringDType(), EXTEND_EXP);
2500 iterateCheck(nodep, "value", nodep->valuep(), CONTEXT_DET, FINAL, vdtypep->subDTypep(),
2501 EXTEND_EXP);
2502 }
2503 }
2504 void visit(AstConsPackUOrStruct* nodep) override {
2505 // Type was computed when constructed in V3Width earlier
2506 auto* const vdtypep = VN_AS(nodep->dtypep()->skipRefp(), NodeUOrStructDType);
2507 UASSERT_OBJ(vdtypep, nodep, "ConsPackUOrStruct requires packed array parent data type");
2508 userIterateChildren(nodep, WidthVP{vdtypep, BOTH}.p());
2509 }
2510 void visit(AstConsPackMember* nodep) override {
2511 // Type was computed when constructed in V3Width earlier
2512 auto* const vdtypep = VN_AS(nodep->dtypep(), MemberDType);
2513 UASSERT_OBJ(vdtypep, nodep, "ConsPackMember requires member data type");
2514 if (m_vup->prelim()) userIterateAndNext(nodep->rhsp(), WidthVP{vdtypep, BOTH}.p());
2515 }
2516 void visit(AstConsDynArray* nodep) override {
2517 // Type computed when constructed here
2518 AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType);
2519 UASSERT_OBJ(vdtypep, nodep, "ConsDynArray requires queue upper parent data type");
2520 if (m_vup->prelim()) {
2521 AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2522 AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2523 userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p());
2524 userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p());
2525 nodep->dtypeFrom(vdtypep);
2526 }
2527 if (m_vup->final()) {
2528 if (nodep->didWidthAndSet()) return;
2529 // Arguments can be either elements of the queue or a queue itself
2530 // Concats (part of tree of concats) must always become ConsDynArray's
2531 AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2532 AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2533 if (nodep->lhsp()) {
2534 if (nodep->lhsIsValue()) {
2535 // Sub elements are not queues, but concats, must always pass concats down
2536 iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL);
2537 } else {
2538 userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p());
2539 }
2540 }
2541 if (nodep->rhsp()) {
2542 if (nodep->rhsIsValue()) {
2543 iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL);
2544 } else {
2545 userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p());
2546 }
2547 }
2548 if (nodep->didWidthAndSet()) return;
2549 nodep->dtypeFrom(vdtypep);
2550 }
2551 }
2552 void visit(AstConsQueue* nodep) override {
2553 // Type computed when constructed here
2554 AstQueueDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), QueueDType);
2555 UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
2556 if (m_vup->prelim()) {
2557 AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2558 AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2559 userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, PRELIM}.p());
2560 userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, PRELIM}.p());
2561 nodep->dtypeFrom(vdtypep);
2562 }
2563 if (m_vup->final()) {
2564 if (nodep->didWidthAndSet()) return;
2565 // Arguments can be either elements of the queue or a queue itself
2566 // Concats (part of tree of concats) must always become ConsQueue's
2567 AstNodeDType* const lhsDtp = nodep->lhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2568 AstNodeDType* const rhsDtp = nodep->rhsIsValue() ? vdtypep->subDTypep() : vdtypep;
2569 if (nodep->lhsp()) {
2570 if (nodep->lhsIsValue()) {
2571 // Sub elements are not queues, but concats, must always pass concats down
2572 iterateCheckTyped(nodep, "LHS", nodep->lhsp(), lhsDtp, FINAL);
2573 } else {
2574 userIterateAndNext(nodep->lhsp(), WidthVP{lhsDtp, FINAL}.p());
2575 }
2576 }
2577 if (nodep->rhsp()) {
2578 if (nodep->rhsIsValue()) {
2579 iterateCheckTyped(nodep, "RHS", nodep->rhsp(), rhsDtp, FINAL);
2580 } else {
2581 userIterateAndNext(nodep->rhsp(), WidthVP{rhsDtp, FINAL}.p());
2582 }
2583 }
2584 nodep->dtypeFrom(vdtypep);
2585 }
2586 }
2587 void visit(AstInitItem* nodep) override { //
2588 userIterateChildren(nodep, m_vup);
2589 }
2590 void visit(AstInitArray* nodep) override {
2591 // InitArray has type of the array; children are array values
2592 if (m_vup->prelim()) { // First stage evaluation
2593 AstNodeDType* const vdtypep = m_vup->dtypeNullp();
2594 UASSERT_OBJ(vdtypep, nodep, "InitArray type not assigned by AstPattern/Var visitor");
2595 nodep->dtypep(vdtypep);
2596 const AstNodeDType* const arrayp = vdtypep->skipRefp();
2597 if (VN_IS(arrayp, NodeArrayDType) || VN_IS(arrayp, AssocArrayDType)) {
2598 userIterateChildren(nodep, WidthVP{arrayp->subDTypep(), BOTH}.p());
2599 } else {
2600 UINFO(1, "on " << nodep << endl);
2601 UINFO(1, "dtype object " << arrayp << endl);
2602 nodep->v3fatalSrc("InitArray on non-array");
2603 }
2604 }
2605 }
2606 void visit(AstDist* nodep) override {
2607 // x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b)
2608 nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
2609 userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
2610 for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2611 nextip = itemp->nextp(); // iterate may cause the node to get replaced
2612 VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp);
2613 }
2614
2615 AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType);
2616 AstNodeDType* subDTypep = nullptr;
2617 nodep->dtypeSetBit();
2618
2619 if (dtype && dtype->isString()) {
2620 nodep->dtypeSetString();
2621 subDTypep = nodep->findStringDType();
2622 } else if (dtype && dtype->isDouble()) {
2623 nodep->dtypeSetDouble();
2624 subDTypep = nodep->findDoubleDType();
2625 } else {
2626 // Take width as maximum across all items
2627 int width = nodep->exprp()->width();
2628 int mwidth = nodep->exprp()->widthMin();
2629 for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
2630 width = std::max(width, itemp->width());
2631 mwidth = std::max(mwidth, itemp->widthMin());
2632 }
2633 subDTypep = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric());
2634 }
2635
2636 iterateCheck(nodep, "Dist expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep,
2637 EXTEND_EXP);
2638 for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2639 nextip = itemp->nextp();
2640 itemp = VN_AS(itemp, DistItem)->rangep();
2641 // InsideRange will get replaced with Lte&Gte and finalized later
2642 if (!VN_IS(itemp, InsideRange))
2643 iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
2644 }
2645 AstNodeExpr* newp = nullptr;
2646 for (AstDistItem* itemp = nodep->itemsp(); itemp;
2647 itemp = VN_AS(itemp->nextp(), DistItem)) {
2648 AstNodeExpr* inewp = insideItem(nodep, nodep->exprp(), itemp->rangep());
2649 if (!inewp) continue;
2650 AstNodeExpr* const cmpp
2651 = new AstGt{itemp->fileline(), itemp->weightp()->unlinkFrBack(),
2652 new AstConst{itemp->fileline(), 0}};
2653 cmpp->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
2654 cmpp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
2655 inewp = new AstLogAnd{itemp->fileline(), cmpp, inewp};
2656 newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
2657 }
2658 if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
2659
2660 if (debug() >= 9) nodep->dumpTree("- dist-out: ");
2661 nodep->replaceWith(newp);
2662 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2663 }
2664
2665 void visit(AstInside* nodep) override {
2666 userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
2667 for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2668 nextip = itemp->nextp(); // iterate may cause the node to get replaced
2669 VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp);
2670 }
2671
2672 AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType);
2673 AstNodeDType* expDTypep = nullptr;
2674
2675 if (dtype && dtype->isString()) {
2676 nodep->dtypeSetString();
2677 expDTypep = nodep->findStringDType();
2678 } else if (dtype && dtype->isDouble()) {
2679 nodep->dtypeSetDouble();
2680 expDTypep = nodep->findDoubleDType();
2681 } else {
2682 // Take width as maximum across all items
2683 int width = nodep->exprp()->width();
2684 int mwidth = nodep->exprp()->widthMin();
2685 bool isFourstate = nodep->exprp()->dtypep()->isFourstate();
2686 for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
2687 width = std::max(width, itemp->width());
2688 mwidth = std::max(mwidth, itemp->widthMin());
2689 isFourstate |= itemp->dtypep()->isFourstate();
2690 }
2691 nodep->dtypeSetBit();
2692 const VSigning numeric = nodep->exprp()->dtypep()->numeric();
2693 expDTypep = isFourstate ? nodep->findLogicDType(width, mwidth, numeric)
2694 : nodep->findBitDType(width, mwidth, numeric);
2695 }
2696
2697 iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT_DET, FINAL, expDTypep,
2698 EXTEND_EXP);
2699 for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2700 nextip = itemp->nextp(); // iterate may cause the node to get replaced
2701 // InsideRange will get replaced with Lte&Gte and finalized later
2702 if (!VN_IS(itemp, InsideRange))
2703 iterateCheck(nodep, "Inside Item", itemp, CONTEXT_DET, FINAL, expDTypep,
2704 EXTEND_EXP);
2705 }
2706
2707 if (debug() >= 9) nodep->dumpTree("- inside-in: ");
2708 // Now rip out the inside and replace with simple math
2709 AstNodeExpr* newp = nullptr;
2710 for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
2711 nextip = VN_AS(itemp->nextp(), NodeExpr); // Will be unlinking
2712 AstNodeExpr* const inewp = insideItem(nodep, nodep->exprp(), itemp);
2713 if (!inewp) continue;
2714 newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
2715 }
2716 if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
2717 if (debug() >= 9) newp->dumpTree("- inside-out: ");
2718 nodep->replaceWith(newp);
2719 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2720 }
2721 AstNodeExpr* insideItem(AstNode* nodep, AstNodeExpr* exprp, AstNodeExpr* itemp) {
2722 const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp();
2723 if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) {
2724 // Similar logic in V3Case
2725 return irangep->newAndFromInside(exprp, irangep->lhsp()->unlinkFrBack(),
2726 irangep->rhsp()->unlinkFrBack());
2727 } else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType)
2728 || VN_IS(itemDtp, QueueDType)) {
2729 // Unsupported in parameters
2730 AstNodeExpr* const cexprp = exprp->cloneTreePure(true);
2731 AstNodeExpr* const inewp
2732 = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside", cexprp};
2733 iterateCheckTyped(nodep, "inside value", cexprp, itemDtp->subDTypep(), BOTH);
2734 VL_DANGLING(cexprp); // Might have been replaced
2735 inewp->dtypeSetBit();
2736 inewp->didWidth(true);
2737 return inewp;
2738 } else if (VN_IS(itemDtp, AssocArrayDType)) {
2739 nodep->v3error("Inside operator not specified on associative arrays "
2740 "(IEEE 1800-2023 11.4.13)");
2741 return nullptr;
2742 }
2743 return AstEqWild::newTyped(itemp->fileline(), exprp->cloneTreePure(true),
2744 itemp->unlinkFrBack());
2745 }
2746 void visit(AstInsideRange* nodep) override {
2747 // Just do each side; AstInside will rip these nodes out later
2748 userIterateAndNext(nodep->lhsp(), m_vup);
2749 userIterateAndNext(nodep->rhsp(), m_vup);
2750 nodep->dtypeFrom(nodep->lhsp());
2751 }
2752
2753 void visit(AstIfaceRefDType* nodep) override {
2754 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2755 UINFO(5, " IFACEREF " << nodep << endl);
2756 userIterateChildren(nodep, m_vup);
2757 nodep->dtypep(nodep);
2758 UINFO(4, "dtWidthed " << nodep << endl);
2759 }
2760 void visit(AstNodeUOrStructDType* nodep) override {
2761 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2762 UINFO(5, " NODEUORS " << nodep << endl);
2763 // if (debug() >= 9) nodep->dumpTree("- class-in: ");
2764 if (!nodep->packed() && v3Global.opt.structsPacked()) nodep->packed(true);
2765 userIterateChildren(nodep, nullptr); // First size all members
2766 nodep->dtypep(nodep);
2767 nodep->isFourstate(false);
2768 // Error checks
2769 for (AstMemberDType* itemp = nodep->membersp(); itemp;
2770 itemp = VN_AS(itemp->nextp(), MemberDType)) {
2771 AstNodeDType* const dtp = itemp->subDTypep()->skipRefp();
2772 if (nodep->packed()
2773 && !dtp->isIntegralOrPacked()
2774 // Historically lax:
2775 && !v3Global.opt.structsPacked())
2776 itemp->v3error("Unpacked data type "
2777 << dtp->prettyDTypeNameQ()
2778 << " in packed struct/union (IEEE 1800-2023 7.2.1)");
2779 if ((VN_IS(nodep, UnionDType) || nodep->packed()) && itemp->valuep()) {
2780 itemp->v3error("Initial values not allowed in packed struct/union"
2781 " (IEEE 1800-2023 7.2.2)");
2782 pushDeletep(itemp->valuep()->unlinkFrBack());
2783 } else if (itemp->valuep()) {
2784 itemp->valuep()->v3warn(E_UNSUPPORTED,
2785 "Unsupported: Initial values in struct/union members");
2786 pushDeletep(itemp->valuep()->unlinkFrBack());
2787 }
2788 }
2789 // Determine bit assignments and width
2790 if (VN_IS(nodep, UnionDType) || nodep->packed()) {
2791 int lsb = 0;
2792 int width = 0;
2793 // Report errors on first member first
2794 AstMemberDType* itemp;
2795 // MSB is first, so loop backwards
2796 for (itemp = nodep->membersp(); itemp && itemp->nextp();
2797 itemp = VN_AS(itemp->nextp(), MemberDType)) {}
2798 for (; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
2799 if (itemp->isFourstate()) nodep->isFourstate(true);
2800 itemp->lsb(lsb);
2801 if (VN_IS(nodep, UnionDType)) {
2802 width = std::max(width, itemp->width());
2803 } else {
2804 lsb += itemp->width();
2805 width += itemp->width();
2806 }
2807 }
2808 nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration
2809 } else {
2810 nodep->widthForce(1, 1);
2811 }
2812 // if (debug() >= 9) nodep->dumpTree("- class-out: ");
2813 }
2814 void visit(AstClass* nodep) override {
2815 if (nodep->didWidthAndSet()) return;
2816
2817 // If the class is std::process
2818 if (nodep->name() == "process") {
2819 AstPackage* const packagep = getItemPackage(nodep);
2820 if (packagep && packagep->name() == "std") {
2821 // Change type of m_process to VlProcessRef
2822 if (AstVar* const varp
2823 = VN_CAST(m_memberMap.findMember(nodep, "m_process"), Var)) {
2824 AstNodeDType* const dtypep = varp->getChildDTypep();
2825 if (!varp->dtypep()) {
2826 VL_DO_DANGLING(pushDeletep(dtypep->unlinkFrBack()), dtypep);
2827 }
2828 AstBasicDType* const newdtypep = new AstBasicDType{
2829 nodep->fileline(), VBasicDTypeKwd::PROCESS_REFERENCE, VSigning::UNSIGNED};
2830 v3Global.rootp()->typeTablep()->addTypesp(newdtypep);
2831 varp->dtypep(newdtypep);
2832 }
2833 // Mark that self requires process instance
2834 if (AstNodeFTask* const ftaskp
2835 = VN_CAST(m_memberMap.findMember(nodep, "self"), NodeFTask)) {
2836 ftaskp->setNeedProcess();
2837 }
2838 }
2839 }
2840
2841 // Must do extends first, as we may in functions under this class
2842 // start following a tree of extends that takes us to other classes
2843 userIterateAndNext(nodep->extendsp(), nullptr);
2844 userIterateChildren(nodep, nullptr); // First size all members
2845 }
2846 void visit(AstThisRef* nodep) override {
2847 if (nodep->didWidthAndSet()) return;
2848 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
2849 }
2850 void visit(AstClassRefDType* nodep) override {
2851 if (nodep->didWidthAndSet()) return;
2852 // TODO this maybe eventually required to properly resolve members,
2853 // though causes problems with t_class_forward.v, so for now avoided
2854 // userIterateChildren(nodep->classp(), nullptr);
2855 }
2856 void visit(AstClassOrPackageRef* nodep) override {
2857 if (nodep->didWidthAndSet()) return;
2858 userIterateChildren(nodep, nullptr);
2859 }
2860 void visit(AstDot* nodep) override {
2861 // We can only reach this from constify called during V3Param (so before linkDotParam)
2862 // ... #(Cls#(...)::...) ...
2863 // ^^~~~ this is our DOT
2864 nodep->v3warn(E_UNSUPPORTED, "dotted expressions in parameters\n"
2865 << nodep->warnMore() << "... Suggest use a typedef");
2866 }
2867 void visit(AstClassExtends* nodep) override {
2868 if (nodep->didWidthAndSet()) return;
2869 if (VN_IS(nodep->childDTypep(), ClassRefDType)) {
2870 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
2871 }
2872 }
2873 void visit(AstMemberDType* nodep) override {
2874 if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
2875 // Iterate into subDTypep() to resolve that type and update pointer.
2876 nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
2877 nodep->widthFromSub(nodep->subDTypep());
2878 nodep->dtypeFrom(nodep->subDTypep());
2879 if (nodep->valuep()) {
2880 userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
2881 iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep());
2882 }
2883 }
2884 void visit(AstStructSel* nodep) override {
2885 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
2886 }
2887 void visit(AstMemberSel* nodep) override {
2888 UINFO(5, " MEMBERSEL " << nodep << endl);
2889 if (nodep->didWidth()) return;
2890 if (debug() >= 9) nodep->dumpTree("- mbs-in: ");
2891 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
2892 if (debug() >= 9) nodep->dumpTree("- mbs-ic: ");
2893 // Find the fromp dtype - should be a class
2894 UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type");
2895 AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
2896 UINFO(9, " from dt " << fromDtp << endl);
2897 const AstMemberSel* const fromSel = VN_CAST(nodep->fromp(), MemberSel);
2898 if (AstClocking* const clockingp = fromSel && fromSel->varp()
2899 ? VN_CAST(fromSel->varp()->firstAbovep(), Clocking)
2900 : nullptr) {
2901 // In: MEMBERSEL{MEMBERSEL{vifaceref, "cb", cb_event}, "clockvar", null}
2902 // Out: MEMBERSEL{vifaceref, "clockvar", clockvar}
2903 UINFO(9, " from clocking " << clockingp << endl);
2904 if (AstVar* const varp = memberSelClocking(nodep, clockingp)) {
2905 if (!varp->didWidth()) userIterate(varp, nullptr);
2906 AstMemberSel* fromp = VN_AS(nodep->fromp(), MemberSel);
2907 fromp->replaceWith(fromp->fromp()->unlinkFrBack());
2908 VL_DO_DANGLING(fromp->deleteTree(), fromp);
2909 nodep->dtypep(varp->dtypep());
2910 nodep->varp(varp);
2911 if (nodep->access().isWriteOrRW()) V3LinkLValue::linkLValueSet(nodep);
2912 if (AstIfaceRefDType* const adtypep
2913 = VN_CAST(nodep->fromp()->dtypep(), IfaceRefDType)) {
2914 nodep->varp()->sensIfacep(adtypep->ifacep());
2915 }
2916 UINFO(9, " done clocking msel " << nodep << endl);
2917 nodep->didWidth(true); // Must not visit again: will confuse scopes
2918 return;
2919 }
2920 } else if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
2921 if (memberSelStruct(nodep, adtypep)) return;
2922 } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
2923 if (memberSelClass(nodep, adtypep)) return;
2924 } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
2925 if (AstNode* foundp = memberSelIface(nodep, adtypep)) {
2926 if (AstClocking* const clockingp = VN_CAST(foundp, Clocking))
2927 foundp = clockingp->ensureEventp();
2928 if (AstVar* const varp = VN_CAST(foundp, Var)) {
2929 if (!varp->didWidth()) userIterate(varp, nullptr);
2930 nodep->dtypep(foundp->dtypep());
2931 nodep->varp(varp);
2932 AstIface* const ifacep = adtypep->ifacep();
2933 varp->sensIfacep(ifacep);
2934 nodep->fromp()->foreach(
2935 [ifacep](AstVarRef* const refp) { refp->varp()->sensIfacep(ifacep); });
2936 nodep->didWidth(true);
2937 return;
2938 }
2939 UINFO(1, "found object " << foundp << endl);
2940 nodep->v3fatalSrc("MemberSel of non-variable\n"
2941 << nodep->warnContextPrimary() << '\n'
2942 << foundp->warnOther() << "... Location of found object\n"
2943 << foundp->warnContextSecondary());
2944 }
2945 } else if (VN_IS(fromDtp, EnumDType) //
2946 || VN_IS(fromDtp, AssocArrayDType) //
2947 || VN_IS(fromDtp, WildcardArrayDType) //
2948 || VN_IS(fromDtp, UnpackArrayDType) //
2949 || VN_IS(fromDtp, DynArrayDType) //
2950 || VN_IS(fromDtp, QueueDType) //
2951 || VN_IS(fromDtp, ConstraintRefDType) //
2952 || VN_IS(fromDtp, BasicDType)) {
2953 // Method call on enum without following parenthesis, e.g. "ENUM.next"
2954 // Convert this into a method call, and let that visitor figure out what to do next
2955 AstNode* const newp = new AstMethodCall{
2956 nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), nullptr};
2957 nodep->replaceWith(newp);
2958 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2959 userIterate(newp, m_vup);
2960 return;
2961 } else {
2962 nodep->v3error("Member selection of non-struct/union object '"
2963 << nodep->fromp()->prettyTypeName() << "' which is a '"
2964 << nodep->fromp()->dtypep()->prettyTypeName() << "'");
2965 }
2966 // Error handling
2967 nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
2968 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2969 }
2970 bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
2971 if (nodep->name() == "rand_mode") {
2972 nodep->replaceWith(new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
2973 "rand_mode", nullptr});
2974 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2975 return true;
2976 }
2977 // Returns true if ok
2978 // No need to width-resolve the class, as it was done when we did the child
2979 AstClass* const first_classp = adtypep->classp();
2980 UASSERT_OBJ(first_classp, nodep, "Unlinked");
2981 for (AstClass* classp = first_classp; classp;) {
2982 if (AstNode* const foundp = m_memberMap.findMember(classp, nodep->name())) {
2983 if (AstVar* const varp = VN_CAST(foundp, Var)) {
2984 if (!varp->didWidth()) userIterate(varp, nullptr);
2985 if (varp->lifetime().isStatic() || varp->isParam()) {
2986 // Static members are moved outside the class, so they shouldn't be
2987 // accessed by member select on a class object
2988 AstVarRef* const varRefp
2989 = new AstVarRef{nodep->fileline(), varp, nodep->access()};
2990 varRefp->classOrPackagep(classp);
2991 nodep->replaceWith(varRefp);
2992 VL_DO_DANGLING(pushDeletep(nodep), nodep);
2993 return true;
2994 }
2995 nodep->dtypep(foundp->dtypep());
2996 nodep->varp(varp);
2997 nodep->didWidth(true);
2998 if (nodep->fromp()->sameTree(m_randomizeFromp) && varp->isRand()) // null-safe
2999 V3LinkLValue::linkLValueSet(nodep);
3000 return true;
3001 }
3002 if (AstConstraint* constrp = VN_CAST(foundp, Constraint)) {
3003 nodep->replaceWith(new AstConstraintRef{
3004 nodep->fileline(), nodep->fromp()->unlinkFrBack(), constrp});
3005 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3006 return true;
3007 }
3008 if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
3009 nodep->replaceWith(adfoundp->cloneTree(false));
3010 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3011 return true;
3012 }
3013 if (VN_IS(foundp, NodeFTask)) {
3014 nodep->replaceWith(new AstMethodCall{nodep->fileline(),
3015 nodep->fromp()->unlinkFrBack(),
3016 nodep->name(), nullptr});
3017 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3018 return true;
3019 }
3020 if (VN_IS(foundp, Constraint)) {
3021 // We don't support constraints yet, so just keep as unlinked for now
3022 // Presumably we'll next see a constraint_mode AstMethodCall
3023 nodep->dtypep(nodep->findConstraintRefDType());
3024 UINFO(9, "Unsupported constraint select " << nodep << endl);
3025 return true;
3026 }
3027 UINFO(1, "found object " << foundp << endl);
3028 nodep->v3fatalSrc("MemberSel of non-variable\n"
3029 << nodep->warnContextPrimary() << '\n'
3030 << foundp->warnOther() << "... Location of found object\n"
3031 << foundp->warnContextSecondary());
3032 }
3033 classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3034 }
3035
3036 VSpellCheck speller;
3037 for (AstClass* classp = first_classp; classp;) {
3038 for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
3039 if (VN_IS(itemp, Constraint) || VN_IS(itemp, EnumItemRef) || VN_IS(itemp, Var)) {
3040 speller.pushCandidate(itemp->prettyName());
3041 }
3042 }
3043 classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3044 }
3045 const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3046 nodep->v3error(
3047 "Member " << nodep->prettyNameQ() << " not found in class "
3048 << first_classp->prettyNameQ() << "\n"
3049 << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3050 return false; // Caller handles error
3051 }
3052 AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
3053 // Returns node if ok
3054 // No need to width-resolve the interface, as it was done when we did the child
3055 AstNodeModule* const ifacep = adtypep->ifacep();
3056 UASSERT_OBJ(ifacep, nodep, "Unlinked");
3057 VSpellCheck speller;
3058 for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
3059 if (itemp->name() == nodep->name()) return itemp;
3060 if (VN_IS(itemp, Var) || VN_IS(itemp, Modport)) {
3061 speller.pushCandidate(itemp->prettyName());
3062 }
3063 }
3064 const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3065 nodep->v3error(
3066 "Member " << nodep->prettyNameQ() << " not found in interface "
3067 << ifacep->prettyNameQ() << "\n"
3068 << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3069 return nullptr; // Caller handles error
3070 }
3071 AstVar* memberSelClocking(AstMemberSel* nodep, AstClocking* clockingp) {
3072 // Returns node if ok
3073 VSpellCheck speller;
3074 for (AstClockingItem* itemp = clockingp->itemsp(); itemp;
3075 itemp = VN_AS(itemp->nextp(), ClockingItem)) {
3076 if (itemp->varp()->name() == nodep->name()) return itemp->varp();
3077 speller.pushCandidate(itemp->varp()->prettyName());
3078 }
3079 const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3080 nodep->v3error(
3081 "Member " << nodep->prettyNameQ() << " not found in clocking block "
3082 << clockingp->prettyNameQ() << "\n"
3083 << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3084 return nullptr; // Caller handles error
3085 }
3086 bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
3087 // Returns true if ok
3088 if (AstMemberDType* const memberp
3089 = VN_CAST(m_memberMap.findMember(adtypep, nodep->name()), MemberDType)) {
3090 if (m_attrp) { // Looking for the base of the attribute
3091 nodep->dtypep(memberp);
3092 UINFO(9, " MEMBERSEL(attr) -> " << nodep << endl);
3093 UINFO(9, " dt-> " << nodep->dtypep() << endl);
3094 } else if (adtypep->packed()) {
3095 AstSel* const newp = new AstSel{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3096 memberp->lsb(), memberp->width()};
3097 // Must skip over the member to find the union; as the member may disappear later
3098 newp->dtypep(memberp->subDTypep()->skipRefToEnump());
3099 newp->didWidth(true); // Don't replace dtype with basic type
3100 UINFO(9, " MEMBERSEL -> " << newp << endl);
3101 UINFO(9, " dt-> " << newp->dtypep() << endl);
3102 nodep->replaceWith(newp);
3103 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3104 // Should be able to treat it as a normal-ish nodesel - maybe.
3105 // The lhsp() will be strange until this stage; create the number here?
3106 } else {
3107 AstStructSel* const newp = new AstStructSel{
3108 nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()};
3109 // Must skip over the member to find the union; as the member may disappear later
3110 newp->dtypep(memberp->subDTypep()->skipRefToEnump());
3111 newp->didWidth(true); // Don't replace dtype with basic type
3112 UINFO(9, " MEMBERSEL -> " << newp << endl);
3113 UINFO(9, " dt-> " << newp->dtypep() << endl);
3114 nodep->replaceWith(newp);
3115 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3116 // Should be able to treat it as a normal-ish nodesel - maybe.
3117 // The lhsp() will be strange until this stage; create the number here?
3118 }
3119 return true;
3120 }
3121 nodep->v3error("Member " << nodep->prettyNameQ() << " not found in structure");
3122 return false;
3123 }
3124
3125 void visit(AstCExpr* nodep) override {
3126 // Inserted by V3Width only so we know has been resolved
3127 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
3128 }
3129 void visit(AstCMethodHard* nodep) override {
3130 // Never created before V3Width, so no need to redo it
3131 UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized");
3132 }
3133
3134 void visit(AstMethodCall* nodep) override {
3135 UINFO(5, " METHODCALL " << nodep << endl);
3136 if (nodep->didWidth()) return;
3137 if (debug() >= 9) nodep->dumpTree("- mts-in: ");
3138 // Should check types the method requires, but at present we don't do much
3139 userIterate(nodep->fromp(), WidthVP{SELF, BOTH}.p());
3140 // Any AstWith is checked later when know types, in methodWithArgument
3141 for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
3142 if (AstArg* const argp = VN_CAST(pinp, Arg)) {
3143 if (argp->exprp())
3144 userIterate(argp->exprp(),
3145 WidthVP{nodep->fromp()->dtypep()->subDTypep(), BOTH}.p());
3146 }
3147 }
3148 // Find the fromp dtype - should be a class
3149 UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
3150 AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
3151 AstBasicDType* const basicp = fromDtp ? fromDtp->basicp() : nullptr;
3152 UINFO(9, " from dt " << fromDtp << endl);
3153 userIterate(fromDtp, WidthVP{SELF, BOTH}.p());
3154 if (nodep->name() == "rand_mode") {
3155 methodCallRandMode(nodep);
3156 } else if (nodep->name() == "constraint_mode") {
3157 methodCallConstraint(nodep, nullptr);
3158 } else if (AstEnumDType* const adtypep = VN_CAST(fromDtp, EnumDType)) {
3159 methodCallEnum(nodep, adtypep);
3160 } else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
3161 methodCallAssoc(nodep, adtypep);
3162 } else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) {
3163 methodCallWildcard(nodep, adtypep);
3164 } else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) {
3165 methodCallDyn(nodep, adtypep);
3166 } else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) {
3167 methodCallQueue(nodep, adtypep);
3168 } else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
3169 methodCallClass(nodep, adtypep);
3170 } else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
3171 methodCallIfaceRef(nodep, adtypep);
3172 } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
3173 methodCallUnpack(nodep, adtypep);
3174 } else if (AstConstraintRefDType* const adtypep = VN_CAST(fromDtp, ConstraintRefDType)) {
3175 methodCallConstraint(nodep, adtypep);
3176 } else if (basicp && basicp->isEvent()) {
3177 methodCallEvent(nodep, basicp);
3178 } else if (basicp && basicp->isString()) {
3179 methodCallString(nodep, basicp);
3180 } else {
3181 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Member call on object '"
3182 << nodep->fromp()->prettyTypeName()
3183 << "' which is a '"
3184 << nodep->fromp()->dtypep()->prettyTypeName() << "'");
3185 nodep->dtypep(m_vup->dtypeNullp());
3186 }
3187 }
3188 AstWith* methodWithArgument(AstNodeFTaskRef* nodep, bool required, bool arbReturn,
3189 AstNodeDType* returnDtp, AstNodeDType* indexDtp,
3190 AstNodeDType* valueDtp) {
3191 UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type");
3192 for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
3193 if (AstWith* const withp = VN_CAST(pinp, With)) {
3194 withp->indexArgRefp()->dtypep(indexDtp);
3195 withp->valueArgRefp()->dtypep(valueDtp);
3196 userIterate(withp, WidthVP{returnDtp, BOTH}.p());
3197 withp->unlinkFrBack();
3198 return withp;
3199 }
3200 }
3201 if (required) {
3202 nodep->v3error("'with' statement is required for ." << nodep->prettyName()
3203 << " method");
3204 }
3205 return nullptr;
3206 }
3207 void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) {
3208 int narg = 0;
3209 for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) {
3210 if (VN_IS(argp, With)) {
3211 argp->v3error("'with' not legal on this method");
3212 // Delete all arguments as nextp() otherwise dangling
3213 VL_DO_DANGLING(pushDeletep(argp->unlinkFrBackWithNext()), argp);
3214 break;
3215 }
3216 ++narg;
3217 UASSERT_OBJ(VN_IS(argp, Arg), nodep, "Method arg without Arg type");
3218 }
3219 const bool ok = (narg >= minArg) && (narg <= maxArg);
3220 if (!ok) {
3221 nodep->v3error("The " << narg << " arguments passed to ." << nodep->prettyName()
3222 << " method does not match its requiring " << cvtToStr(minArg)
3223 << (minArg == maxArg ? "" : " to " + cvtToStr(maxArg))
3224 << " arguments");
3225 // Adjust to required argument counts, very bogus, but avoids core dump
3226 for (; narg < minArg; ++narg) {
3227 nodep->addPinsp(
3228 new AstArg{nodep->fileline(), "", new AstConst(nodep->fileline(), 0)});
3229 }
3230 for (; narg > maxArg; --narg) {
3231 AstNode* argp = nodep->pinsp();
3232 while (argp->nextp()) argp = argp->nextp();
3233 argp->unlinkFrBack();
3234 VL_DO_DANGLING(argp->deleteTree(), argp);
3235 }
3236 }
3237 }
3238
3239 void methodCallEnum(AstMethodCall* nodep, AstEnumDType* adtypep) {
3240 // Method call on enum without following parenthesis, e.g. "ENUM.next"
3241 // Convert this into a method call, and let that visitor figure out what to do next
3242 if (nodep->name() == "num" //
3243 || nodep->name() == "first" //
3244 || nodep->name() == "last") {
3245 // Constant value
3246 AstConst* newp = nullptr;
3247 methodOkArguments(nodep, 0, 0);
3248 if (nodep->name() == "num") {
3249 int items = 0;
3250 for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items;
3251 newp = new AstConst(nodep->fileline(), AstConst::Signed32{}, items);
3252 } else if (nodep->name() == "first") {
3253 const AstEnumItem* itemp = adtypep->itemsp();
3254 if (!itemp) {
3255 newp = new AstConst(nodep->fileline(), AstConst::Signed32{},
3256 0); // Spec doesn't say what to do
3257 } else {
3258 newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const
3259 newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
3260 }
3261 } else if (nodep->name() == "last") {
3262 const AstEnumItem* itemp = adtypep->itemsp();
3263 while (itemp && itemp->nextp()) itemp = VN_AS(itemp->nextp(), EnumItem);
3264 if (!itemp) {
3265 newp = new AstConst(nodep->fileline(), AstConst::Signed32{},
3266 0); // Spec doesn't say what to do
3267 } else {
3268 newp = VN_AS(itemp->valuep()->cloneTree(false), Const); // A const
3269 newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
3270 }
3271 }
3272 UASSERT_OBJ(newp, nodep, "Enum method (perhaps enum item) not const");
3273 newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer;
3274 // may have warning disables
3275 nodep->replaceWith(newp);
3276 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3277 } else if (nodep->name() == "name" || nodep->name() == "next" || nodep->name() == "prev") {
3278 VAttrType attrType;
3279 if (nodep->name() == "name") {
3280 attrType = VAttrType::ENUM_NAME;
3281 methodOkArguments(nodep, 0, 0);
3282 } else if (nodep->name() == "next") {
3283 attrType = VAttrType::ENUM_NEXT;
3284 methodOkArguments(nodep, 0, 1);
3285 } else if (nodep->name() == "prev") {
3286 attrType = VAttrType::ENUM_PREV;
3287 methodOkArguments(nodep, 0, 1);
3288 } else {
3289 nodep->v3fatalSrc("Bad case");
3290 }
3291
3292 if (nodep->name() != "name" && nodep->pinsp()) {
3293 if (!VN_IS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)) {
3294 nodep->pinsp()->v3fatalSrc(
3295 "Unsupported: enum next/prev with non-const argument");
3296 } else {
3297 const uint32_t stepWidth
3298 = VN_AS(VN_AS(nodep->pinsp(), Arg)->exprp(), Const)->toUInt();
3299 if (stepWidth == 0) {
3300 // Step of 0 "legalizes" like $cast, use .next.prev
3301 AstMethodCall* const newp = new AstMethodCall{
3302 nodep->fileline(),
3303 new AstMethodCall{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3304 "next", nullptr},
3305 "prev", nullptr};
3306 nodep->replaceWith(newp);
3307 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3308 return;
3309 } else if (stepWidth != 1) {
3310 // Unroll of enumVar.next(k) to enumVar.next(1).next(k - 1)
3311 AstMethodCall* const clonep = nodep->cloneTree(false);
3312 VN_AS(VN_AS(clonep->pinsp(), Arg)->exprp(), Const)->num().setLong(1);
3313 AstConst* const constp = new AstConst(nodep->fileline(), stepWidth - 1);
3314 AstArg* const argp = new AstArg{nodep->fileline(), "", constp};
3315 AstMethodCall* const newp
3316 = new AstMethodCall{nodep->fileline(), clonep, nodep->name(), argp};
3317 nodep->replaceWith(newp);
3318 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3319 return;
3320 }
3321 }
3322 }
3323 AstNodeExpr* const newp
3324 = enumSelect(nodep->fromp()->unlinkFrBack(), adtypep, attrType);
3325 nodep->replaceWith(newp);
3326 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3327 } else {
3328 nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
3329 }
3330 }
3331 void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
3332 AstCMethodHard* newp = nullptr;
3333 if (nodep->name() == "num" // function int num()
3334 || nodep->name() == "size") {
3335 methodOkArguments(nodep, 0, 0);
3336 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3337 "size"}; // So don't need num()
3338 newp->dtypeSetSigned32();
3339 } else if (nodep->name() == "first" // function int first(ref index)
3340 || nodep->name() == "last" //
3341 || nodep->name() == "next" //
3342 || nodep->name() == "prev" //
3343 || nodep->name() == "unique_index" //
3344 || nodep->name() == "find_index" || nodep->name() == "find_first_index"
3345 || nodep->name() == "find_last_index") {
3346 nodep->v3error("Array method " << nodep->prettyNameQ()
3347 << " not legal on wildcard associative arrays");
3348 } else if (nodep->name() == "exists") { // function int exists(input index)
3349 // IEEE really should have made this a "bit" return
3350 methodOkArguments(nodep, 1, 1);
3351 AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
3352 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists",
3353 index_exprp->unlinkFrBack()};
3354 newp->dtypeSetSigned32();
3355 } else if (nodep->name() == "delete") { // function void delete([input integer index])
3356 methodOkArguments(nodep, 0, 1);
3357 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3358 if (!nodep->pinsp()) {
3359 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3360 "clear"};
3361 newp->dtypeSetVoid();
3362 } else {
3363 AstNodeExpr* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
3364 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3365 "erase", index_exprp->unlinkFrBack()};
3366 newp->dtypeSetVoid();
3367 }
3368 } else if (nodep->name() == "sort" || nodep->name() == "rsort"
3369 || nodep->name() == "reverse" || nodep->name() == "shuffle") {
3370 nodep->v3error("Array method " << nodep->prettyNameQ()
3371 << " not legal on associative arrays");
3372 } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
3373 || nodep->name() == "sum" || nodep->name() == "product") {
3374 // All value return
3375 AstWith* const withp
3376 = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
3377 adtypep->findStringDType(), adtypep->subDTypep());
3378 methodOkArguments(nodep, 0, 0);
3379 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3380 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3381 "r_" + nodep->name(), withp};
3382 newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
3383 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3384 } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") {
3385 methodOkArguments(nodep, 0, 0);
3386 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3387 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3388 nodep->name()};
3389 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3390 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3391 } else if (nodep->name() == "find" || nodep->name() == "find_first"
3392 || nodep->name() == "find_last") {
3393 AstWith* const withp
3394 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
3395 adtypep->findStringDType(), adtypep->subDTypep());
3396 methodOkArguments(nodep, 0, 0);
3397 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3398 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3399 nodep->name(), withp};
3400 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3401 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3402 } else if (nodep->name() == "map") {
3403 nodep->v3warn(E_UNSUPPORTED,
3404 "Unsupported: Wildcard array 'map' method (IEEE 1800-2023 7.12.5)");
3405 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3406 } else {
3407 nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ());
3408 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3409 }
3410 if (newp) {
3411 newp->protect(false);
3412 newp->didWidth(true);
3413 nodep->replaceWith(newp);
3414 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3415 }
3416 }
3417 void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
3418 AstCMethodHard* newp = nullptr;
3419 if (nodep->name() == "num" // function int num()
3420 || nodep->name() == "size") {
3421 methodOkArguments(nodep, 0, 0);
3422 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3423 "size"}; // So don't need num()
3424 newp->dtypeSetSigned32();
3425 } else if (nodep->name() == "first" // function int first(ref index)
3426 || nodep->name() == "last" //
3427 || nodep->name() == "next" //
3428 || nodep->name() == "prev") {
3429 methodOkArguments(nodep, 1, 1);
3430 AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
3431 methodCallLValueRecurse(nodep, index_exprp, VAccess::READWRITE);
3432 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3433 nodep->name(), // first/last/next/prev
3434 index_exprp->unlinkFrBack()};
3435 newp->dtypeSetSigned32();
3436 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3437 } else if (nodep->name() == "exists") { // function int exists(input index)
3438 // IEEE really should have made this a "bit" return
3439 methodOkArguments(nodep, 1, 1);
3440 AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
3441 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists",
3442 index_exprp->unlinkFrBack()};
3443 newp->dtypeSetSigned32();
3444 } else if (nodep->name() == "delete") { // function void delete([input integer index])
3445 methodOkArguments(nodep, 0, 1);
3446 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3447 if (!nodep->pinsp()) {
3448 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3449 "clear"};
3450 newp->dtypeSetVoid();
3451 } else {
3452 AstNodeExpr* const index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
3453 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3454 "erase", index_exprp->unlinkFrBack()};
3455 newp->dtypeSetVoid();
3456 }
3457 } else if (nodep->name() == "sort" || nodep->name() == "rsort"
3458 || nodep->name() == "reverse" || nodep->name() == "shuffle") {
3459 nodep->v3error("Array method " << nodep->prettyNameQ()
3460 << " not legal on associative arrays");
3461 } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
3462 || nodep->name() == "sum" || nodep->name() == "product") {
3463 // All value return
3464 AstWith* const withp = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
3465 adtypep->keyDTypep(), adtypep->subDTypep());
3466 methodOkArguments(nodep, 0, 0);
3467 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3468 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3469 "r_" + nodep->name(), withp};
3470 newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
3471 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3472 } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
3473 || nodep->name() == "unique_index") {
3474 AstWith* const withp = methodWithArgument(
3475 nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
3476 methodOkArguments(nodep, 0, 0);
3477 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3478 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3479 nodep->name(), withp};
3480 if (nodep->name() == "unique_index") {
3481 newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
3482 } else {
3483 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3484 }
3485 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3486 } else if (nodep->name() == "find" || nodep->name() == "find_first"
3487 || nodep->name() == "find_last") {
3488 AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(),
3489 adtypep->keyDTypep(), adtypep->subDTypep());
3490 methodOkArguments(nodep, 0, 0);
3491 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3492 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3493 nodep->name(), withp};
3494 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3495 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3496 } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
3497 || nodep->name() == "find_last_index") {
3498 AstWith* const withp = methodWithArgument(nodep, true, false, nodep->findBitDType(),
3499 adtypep->keyDTypep(), adtypep->subDTypep());
3500 methodOkArguments(nodep, 0, 0);
3501 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3502 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3503 nodep->name(), withp};
3504 newp->dtypep(queueDTypeIndexedBy(adtypep->keyDTypep()));
3505 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3506 } else if (nodep->name() == "map") {
3507 nodep->v3warn(E_UNSUPPORTED,
3508 "Unsupported: Associative array 'map' method (IEEE 1800-2023 7.12.5)");
3509 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3510 } else {
3511 nodep->v3error("Unknown built-in associative array method " << nodep->prettyNameQ());
3512 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3513 }
3514 if (newp) {
3515 newp->protect(false);
3516 newp->didWidth(true);
3517 nodep->replaceWith(newp);
3518 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3519 }
3520 }
3521 AstNodeExpr* methodCallAssocIndexExpr(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
3522 AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
3523 iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->keyDTypep(),
3524 EXTEND_EXP);
3525 VL_DANGLING(index_exprp); // May have been edited
3526 return VN_AS(nodep->pinsp(), Arg)->exprp();
3527 }
3528 AstNodeExpr* methodCallWildcardIndexExpr(AstMethodCall* nodep,
3529 AstWildcardArrayDType* adtypep) {
3530 AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
3531 iterateCheck(nodep, "index", index_exprp, CONTEXT_DET, FINAL, adtypep->findStringDType(),
3532 EXTEND_EXP);
3533 VL_DANGLING(index_exprp); // May have been edited
3534 return VN_AS(nodep->pinsp(), Arg)->exprp();
3535 }
3536 void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
3537 if (AstCMethodHard* const ichildp = VN_CAST(childp, CMethodHard)) {
3538 const std::string name = ichildp->name();
3539 if (name == "at" || name == "atWrite" || name == "atBack" || name == "atWriteAppend"
3540 || name == "atWriteAppendBack") {
3541 const AstNodeDType* const fromDtypep = ichildp->fromp()->dtypep()->skipRefp();
3542 if (VN_IS(fromDtypep, QueueDType) || VN_IS(fromDtypep, DynArrayDType)) {
3543 // Change access methods to writable ones
3544 if (name == "at") {
3545 ichildp->name("atWrite");
3546 } else if (name == "atBack") {
3547 ichildp->name("atWriteAppendBack");
3548 }
3549 }
3550 methodCallLValueRecurse(nodep, ichildp->fromp(), access);
3551 return;
3552 }
3553 }
3554 if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) {
3555 varrefp->access(access);
3556 } else if (const AstMemberSel* const ichildp = VN_CAST(childp, MemberSel)) {
3557 methodCallLValueRecurse(nodep, ichildp->fromp(), access);
3558 } else if (const AstStructSel* const ichildp = VN_CAST(childp, StructSel)) {
3559 methodCallLValueRecurse(nodep, ichildp->fromp(), access);
3560 } else if (const AstNodeSel* const ichildp = VN_CAST(childp, NodeSel)) {
3561 methodCallLValueRecurse(nodep, ichildp->fromp(), access);
3562 } else {
3563 UINFO(1, " Related node: " << childp << endl);
3564 nodep->v3warn(E_UNSUPPORTED, "Unsupported: Non-variable on LHS of built-in method '"
3565 << nodep->prettyName() << "'");
3566 }
3567 }
3568 AstCMethodHard* methodCallArray(AstMethodCall* nodep, AstNodeDType* adtypep) {
3569 AstCMethodHard* newp = nullptr;
3570
3571 if (nodep->name() == "reverse" || nodep->name() == "shuffle" || nodep->name() == "sort"
3572 || nodep->name() == "rsort") {
3573 AstWith* withp = nullptr;
3574 if (nodep->name() == "sort" || nodep->name() == "rsort") {
3575 withp = methodWithArgument(nodep, false, true, nullptr, nodep->findUInt32DType(),
3576 adtypep->subDTypep());
3577 }
3578 methodOkArguments(nodep, 0, 0);
3579 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3580 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3581 nodep->name(), withp};
3582 newp->dtypeSetVoid();
3583 } else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique"
3584 || nodep->name() == "unique_index") {
3585 AstWith* const withp = methodWithArgument(
3586 nodep, false, true, nullptr, nodep->findUInt32DType(), adtypep->subDTypep());
3587 methodOkArguments(nodep, 0, 0);
3588 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3589 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3590 nodep->name(), withp};
3591 if (nodep->name() == "unique_index") {
3592 newp->dtypep(newp->findQueueIndexDType());
3593 } else {
3594 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3595 }
3596 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3597 } else if (nodep->name() == "find" || nodep->name() == "find_first"
3598 || nodep->name() == "find_last" || nodep->name() == "find_index") {
3599 AstWith* const withp
3600 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
3601 nodep->findUInt32DType(), adtypep->subDTypep());
3602 methodOkArguments(nodep, 0, 0);
3603 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3604 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3605 nodep->name(), withp};
3606 newp->dtypep(queueDTypeIndexedBy(adtypep->subDTypep()));
3607 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3608 } else if (nodep->name() == "find_index" || nodep->name() == "find_first_index"
3609 || nodep->name() == "find_last_index") {
3610 AstWith* const withp
3611 = methodWithArgument(nodep, true, false, nodep->findBitDType(),
3612 nodep->findUInt32DType(), adtypep->subDTypep());
3613 methodOkArguments(nodep, 0, 0);
3614 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3615 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3616 nodep->name(), withp};
3617 newp->dtypep(newp->findQueueIndexDType());
3618 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3619 } else if (nodep->name() == "map") {
3620 nodep->v3warn(E_UNSUPPORTED,
3621 "Unsupported: Array 'map' method (IEEE 1800-2023 7.12.5)");
3622 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3623 }
3624 return newp;
3625 }
3626 void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) {
3627 AstCMethodHard* newp = nullptr;
3628 if (nodep->name() == "at") { // Created internally for []
3629 methodOkArguments(nodep, 1, 1);
3630 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at"};
3631 newp->dtypeFrom(adtypep->subDTypep());
3632 } else if (nodep->name() == "atWrite") { // Created internally for []
3633 methodOkArguments(nodep, 1, 1);
3634 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3635 newp
3636 = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "atWrite"};
3637 newp->dtypeFrom(adtypep->subDTypep());
3638 } else if (nodep->name() == "size") {
3639 methodOkArguments(nodep, 0, 0);
3640 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"};
3641 newp->dtypeSetSigned32();
3642 } else if (nodep->name() == "delete") { // function void delete()
3643 methodOkArguments(nodep, 0, 0);
3644 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3645 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "clear"};
3646 newp->dtypeSetVoid();
3647 } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
3648 || nodep->name() == "sum" || nodep->name() == "product") {
3649 // All value return
3650 AstWith* const withp
3651 = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
3652 nodep->findUInt32DType(), adtypep->subDTypep());
3653 methodOkArguments(nodep, 0, 0);
3654 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3655 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3656 "r_" + nodep->name(), withp};
3657 newp->dtypeFrom(adtypep->subDTypep());
3658 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3659 } else if ((newp = methodCallArray(nodep, adtypep))) {
3660 } else {
3661 nodep->v3warn(E_UNSUPPORTED, "Unsupported/unknown built-in dynamic array method "
3662 << nodep->prettyNameQ());
3663 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3664 }
3665 if (newp) {
3666 newp->protect(false);
3667 newp->didWidth(true);
3668 nodep->replaceWith(newp);
3669 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3670 }
3671 }
3672 void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) {
3673 AstCMethodHard* newp = nullptr;
3674 if (nodep->name() == "at" || nodep->name() == "atBack") { // Created internally for []
3675 methodOkArguments(nodep, 1, 1);
3676 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3677 nodep->name()};
3678 newp->dtypeFrom(adtypep->subDTypep());
3679 } else if (nodep->name() == "atWriteAppend"
3680 || nodep->name() == "atWriteAppendBack") { // Created internally for []
3681 methodOkArguments(nodep, 1, 1);
3682 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3683 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3684 nodep->name()};
3685 newp->dtypeFrom(adtypep->subDTypep());
3686 } else if (nodep->name() == "num" // function int num()
3687 || nodep->name() == "size") {
3688 methodOkArguments(nodep, 0, 0);
3689 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"};
3690 newp->dtypeSetSigned32();
3691 } else if (nodep->name() == "delete") { // function void delete([input integer index])
3692 methodOkArguments(nodep, 0, 1);
3693 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3694 if (!nodep->pinsp()) {
3695 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3696 "clear"};
3697 newp->dtypeSetVoid();
3698 } else {
3699 AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep);
3700 if (index_exprp->isZero()) { // delete(0) is a pop_front
3701 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3702 "pop_front"};
3703 newp->dtypeFrom(adtypep->subDTypep());
3704 newp->dtypeSetVoid();
3705 } else {
3706 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3707 "erase", index_exprp->unlinkFrBack()};
3708 newp->dtypeSetVoid();
3709 }
3710 }
3711 } else if (nodep->name() == "insert") {
3712 methodOkArguments(nodep, 2, 2);
3713 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3714 AstNodeExpr* const index_exprp = methodCallQueueIndexExpr(nodep);
3715 AstArg* const argp = VN_AS(nodep->pinsp()->nextp(), Arg);
3716 iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH);
3717 if (index_exprp->isZero()) { // insert(0, ...) is a push_front
3718 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3719 "push_front", argp->exprp()->unlinkFrBack()};
3720 newp->dtypeSetVoid();
3721 } else {
3722 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3723 nodep->name(), index_exprp->unlinkFrBack()};
3724 newp->addPinsp(argp->exprp()->unlinkFrBack());
3725 newp->dtypeSetVoid();
3726 }
3727 } else if (nodep->name() == "pop_front" || nodep->name() == "pop_back") {
3728 methodOkArguments(nodep, 0, 0);
3729 // Returns element, so method both consumes (reads) and modifies the queue
3730 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READWRITE);
3731 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3732 nodep->name()};
3733 newp->dtypeFrom(adtypep->subDTypep());
3734 if (nodep->isStandaloneBodyStmt()) newp->dtypeSetVoid();
3735 } else if (nodep->name() == "push_back" || nodep->name() == "push_front") {
3736 methodOkArguments(nodep, 1, 1);
3737 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3738 AstArg* const argp = VN_AS(nodep->pinsp(), Arg);
3739 iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH);
3740 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3741 nodep->name(), argp->exprp()->unlinkFrBack()};
3742 newp->dtypeSetVoid();
3743 } else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
3744 || nodep->name() == "sum" || nodep->name() == "product") {
3745 AstWith* const withp
3746 = methodWithArgument(nodep, false, false, adtypep->subDTypep(),
3747 nodep->findUInt32DType(), adtypep->subDTypep());
3748 methodOkArguments(nodep, 0, 0);
3749 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
3750 newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
3751 "r_" + nodep->name(), withp};
3752 newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
3753 if (!nodep->firstAbovep()) newp->dtypeSetVoid();
3754 } else if ((newp = methodCallArray(nodep, adtypep))) {
3755 } else {
3756 nodep->v3warn(E_UNSUPPORTED,
3757 "Unsupported/unknown built-in queue method " << nodep->prettyNameQ());
3758 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
3759 }
3760 if (newp) {
3761 newp->protect(false);
3762 newp->didWidth(true);
3763 nodep->replaceWith(newp);
3764 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3765 }
3766 }
3767 AstNodeExpr* methodCallQueueIndexExpr(AstMethodCall* nodep) {
3768 AstNode* const index_exprp = VN_AS(nodep->pinsp(), Arg)->exprp();
3769 iterateCheckSigned32(nodep, "index", index_exprp, BOTH);
3770 VL_DANGLING(index_exprp); // May have been edited
3771 return VN_AS(nodep->pinsp(), Arg)->exprp();
3772 }
3773 void methodCallWarnTiming(AstNodeFTaskRef* const nodep, const std::string& className) {
3774 if (v3Global.opt.timing().isSetFalse()) {
3775 nodep->v3warn(E_NOTIMING,
3776 className << "::" << nodep->name() << "() requires --timing");
3777 } else if (!v3Global.opt.timing().isSetTrue()) {
3778 nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
3779 << className << "::" << nodep->name()
3780 << "() should be handled");
3781 }
3782 }
3783 void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) {
3784 AstIface* const ifacep = adtypep->ifacep();
3785 UINFO(5, __FUNCTION__ << ":" << nodep << endl);
3786 if (AstNodeFTask* const ftaskp
3787 = VN_CAST(m_memberMap.findMember(ifacep, nodep->name()), NodeFTask)) {
3788 UINFO(5, __FUNCTION__ << "AstNodeFTask" << nodep << endl);
3789 userIterate(ftaskp, nullptr);
3790 if (ftaskp->isStatic()) {
3791 AstNodeExpr* argsp = nullptr;
3792 if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
3793 AstNodeFTaskRef* newp = nullptr;
3794 if (VN_IS(ftaskp, Task)) {
3795 newp = new AstTaskRef{nodep->fileline(), VN_AS(ftaskp, Task), argsp};
3796 } else {
3797 newp = new AstFuncRef{nodep->fileline(), VN_AS(ftaskp, Func), argsp};
3798 }
3799 newp->classOrPackagep(ifacep);
3800 nodep->replaceWith(newp);
3801 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3802 } else {
3803 nodep->taskp(ftaskp);
3804 nodep->dtypeFrom(ftaskp);
3805 nodep->classOrPackagep(ifacep);
3806 if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid();
3807 processFTaskRefArgs(nodep);
3808 }
3809 return;
3810 }
3811 nodep->v3error("Member reference from interface to "
3812 << nodep->prettyNameQ() << " is not referencing a valid task or function ");
3813 }
3814 void handleRandomizeArgs(AstNodeFTaskRef* const nodep, AstClass* const classp) {
3815 bool hasNonNullArgs = false;
3816 AstConst* nullp = nullptr;
3817 for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
3818 nextp = pinp->nextp();
3819 AstArg* const argp = VN_CAST(pinp, Arg);
3820 if (!argp) continue;
3821 AstVar* randVarp = nullptr;
3822 AstNodeExpr* exprp = argp->exprp();
3823 if (AstConst* const constp = VN_CAST(exprp, Const)) {
3824 if (constp->num().isNull()) {
3825 nullp = constp;
3826 continue;
3827 }
3828 }
3829 hasNonNullArgs = true;
3830 AstVar* fromVarp = nullptr; // If it's a method call, the leftmost element
3831 // of the dot hierarchy
3832 if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
3833 AstNodeExpr* fromp = methodCallp->fromp();
3834 while (AstMemberSel* const memberSelp = VN_CAST(fromp, MemberSel)) {
3835 fromp = memberSelp->fromp();
3836 }
3837 AstVarRef* const varrefp = VN_AS(fromp, VarRef);
3838 fromVarp = varrefp->varp();
3839 }
3840 if (!VN_IS(exprp, VarRef) && !VN_IS(exprp, MemberSel)) {
3841 argp->v3error("'randomize()' argument must be a variable contained in "
3842 << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
3843 VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
3844 continue;
3845 }
3846 while (exprp) {
3847 if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
3848 randVarp = memberSelp->varp();
3849 exprp = memberSelp->fromp();
3850 } else {
3851 if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
3852 randVarp = varrefp->varp();
3853 } else {
3854 argp->v3warn(
3855 E_UNSUPPORTED,
3856 "Unsupported: Non-variable expression as 'randomize()' argument");
3857 VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
3858 }
3859 exprp = nullptr;
3860 }
3861 // All variables in the dot hierarchy must be randomizable
3862 if (randVarp && !randVarp->isRand()) randVarp->rand(VRandAttr::RAND_INLINE);
3863 }
3864 if (!argp) continue; // Errored out, bail
3865 // randVarp is now the leftmost element from the dot hierarchy in argp->exprp()
3866 if (randVarp == fromVarp) {
3867 // The passed in variable is MemberSel'ected from the MethodCall target
3868 } else if (classp->existsMember([&](const AstClass*, const AstVar* memberVarp) {
3869 return memberVarp == randVarp;
3870 })) {
3871 // The passed in variable is contained in the method call target
3872 } else {
3873 // Passed in a constant or complex expression, or the above conditions are not
3874 // met
3875 argp->v3error("'randomize()' argument must be a variable contained in "
3876 << (fromVarp ? fromVarp->prettyNameQ() : classp->prettyNameQ()));
3877 VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
3878 }
3879 }
3880 if (nullp) {
3881 if (hasNonNullArgs) {
3882 nullp->v3error("Cannot pass more arguments to 'randomize(null)'");
3883 } else {
3884 nullp->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize(null)'");
3885 }
3886 }
3887 }
3888 void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
3889 // No need to width-resolve the class, as it was done when we did the child
3890 AstClass* const first_classp = adtypep->classp();
3891 AstWith* withp = nullptr;
3892 if (nodep->name() == "randomize") {
3893 VL_RESTORER(m_randomizeFromp);
3894 m_randomizeFromp = nodep->fromp();
3895 withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
3896 adtypep->findBitDType(), adtypep);
3897 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3898 V3Randomize::newRandomizeFunc(m_memberMap, first_classp);
3899 handleRandomizeArgs(nodep, first_classp);
3900 } else if (nodep->name() == "srandom") {
3901 methodOkArguments(nodep, 1, 1);
3902 methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
3903 V3Randomize::newSRandomFunc(m_memberMap, first_classp);
3904 }
3905 UASSERT_OBJ(first_classp, nodep, "Unlinked");
3906 for (AstClass* classp = first_classp; classp;) {
3907 if (nodep->fileline()->timingOn()) {
3908 if (classp->name() == "semaphore" || classp->name() == "process"
3909 || VString::startsWith(classp->name(), "mailbox")) {
3910 // Find the package the class is in
3911 AstPackage* const packagep = getItemPackage(classp);
3912 // Check if it's std
3913 if (packagep && packagep->name() == "std") {
3914 if (classp->name() == "process") {
3915 methodCallWarnTiming(nodep, "process");
3916 } else if (classp->name() == "semaphore" && nodep->name() == "get") {
3917 methodCallWarnTiming(nodep, "semaphore");
3918 } else if (nodep->name() == "put" || nodep->name() == "get"
3919 || nodep->name() == "peek") {
3920 methodCallWarnTiming(nodep, "mailbox");
3921 }
3922 }
3923 }
3924 }
3925 if (AstNodeFTask* const ftaskp
3926 = VN_CAST(m_memberMap.findMember(classp, nodep->name()), NodeFTask)) {
3927 userIterate(ftaskp, nullptr);
3928 if (ftaskp->isStatic()) {
3929 AstNodeExpr* argsp = nullptr;
3930 if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
3931 AstNodeFTaskRef* newp = nullptr;
3932 // We use m_vup to determine task or function, so that later error checks
3933 // for funcref->task and taskref->func will pick up properly
3934 if (!m_vup) { // Called as task
3935 newp = new AstTaskRef{nodep->fileline(), nodep->name(), argsp};
3936 } else {
3937 newp = new AstFuncRef{nodep->fileline(), nodep->name(), argsp};
3938 }
3939 newp->taskp(ftaskp); // Not passed above as might be func vs task mismatch
3940 newp->classOrPackagep(classp);
3941 nodep->replaceWith(newp);
3942 VL_DO_DANGLING(nodep->deleteTree(), nodep);
3943 } else {
3944 nodep->taskp(ftaskp);
3945 nodep->dtypeFrom(ftaskp);
3946 nodep->classOrPackagep(classp);
3947 if (VN_IS(ftaskp, Task)) {
3948 if (!m_vup) {
3949 nodep->dtypeSetVoid();
3950 } else {
3951 if (m_vup->prelim()) {
3952 nodep->v3error(
3953 "Cannot call a task/void-function as a member function: "
3954 << nodep->prettyNameQ());
3955 }
3956 nodep->dtypeSetVoid();
3957 }
3958 }
3959 if (withp) nodep->addPinsp(withp);
3960 processFTaskRefArgs(nodep);
3961 }
3962 return;
3963 } else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") {
3964 // See implementations under AstNodeFTaskRef
3965 nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'get_randstate'/'set_randstate' called "
3966 "on object. Suggest call from inside class.");
3967 nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}});
3968 VL_DO_DANGLING(pushDeletep(nodep), nodep);
3969 return;
3970 } else if (nodep->name() == "constraint_mode") {
3971 v3Global.useRandomizeMethods(true);
3972 nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
3973 }
3974 classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3975 }
3976 {
3977 VSpellCheck speller;
3978 for (AstClass* classp = first_classp; classp;) {
3979 for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
3980 if (VN_IS(itemp, NodeFTask)) speller.pushCandidate(itemp->prettyName());
3981 }
3982 classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
3983 }
3984 const string suggest = speller.bestCandidateMsg(nodep->prettyName());
3985 nodep->v3error("Class method "
3986 << nodep->prettyNameQ() << " not found in class "
3987 << first_classp->prettyNameQ() << "\n"
3988 << (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
3989 }
3990 nodep->dtypeSetSigned32(); // Guess on error
3991 }
3992 void methodCallConstraint(AstMethodCall* nodep, AstConstraintRefDType*) {
3993 if (nodep->name() == "constraint_mode") {
3994 // IEEE 1800-2023 18.9
3995 methodOkArguments(nodep, 0, 1);
3996 if (nodep->pinsp()) {
3997 nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
3998 } else {
3999 nodep->dtypeSetVoid();
4000 }
4001 v3Global.useRandomizeMethods(true);
4002 } else {
4003 nodep->v3error("No such constraint method " << nodep->prettyNameQ());
4004 nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
4005 VL_DO_DANGLING(pushDeletep(nodep), nodep);
4006 }
4007 }
4008 void methodCallRandMode(AstMethodCall* nodep) {
4009 methodOkArguments(nodep, 0, 1);
4010 // IEEE 1800-2023 18.8
4011 if (nodep->pinsp()) {
4012 nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
4013 } else {
4014 nodep->dtypeSetVoid();
4015 }
4016 v3Global.useRandomizeMethods(true);
4017 }
4018 void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
4019 enum : uint8_t {
4020 UNKNOWN = 0,
4021 ARRAY_OR,
4022 ARRAY_AND,
4023 ARRAY_XOR,
4024 ARRAY_SUM,
4025 ARRAY_PRODUCT
4026 } methodId;
4027
4028 methodId = UNKNOWN;
4029 if (nodep->name() == "or") {
4030 methodId = ARRAY_OR;
4031 } else if (nodep->name() == "and") {
4032 methodId = ARRAY_AND;
4033 } else if (nodep->name() == "xor") {
4034 methodId = ARRAY_XOR;
4035 } else if (nodep->name() == "sum") {
4036 methodId = ARRAY_SUM;
4037 } else if (nodep->name() == "product") {
4038 methodId = ARRAY_PRODUCT;
4039 }
4040
4041 if (methodId) {
4042 methodOkArguments(nodep, 0, 0);
4043 FileLine* const fl = nodep->fileline();
4044 AstNodeExpr* newp = nullptr;
4045 for (int i = 0; i < adtypep->elementsConst(); ++i) {
4046 AstNodeExpr* const arrayRef = nodep->fromp()->cloneTreePure(false);
4047 AstNodeExpr* const selector = new AstArraySel{fl, arrayRef, i};
4048 if (!newp) {
4049 newp = selector;
4050 } else {
4051 switch (methodId) {
4052 case ARRAY_OR: newp = new AstOr{fl, newp, selector}; break;
4053 case ARRAY_AND: newp = new AstAnd{fl, newp, selector}; break;
4054 case ARRAY_XOR: newp = new AstXor{fl, newp, selector}; break;
4055 case ARRAY_SUM: newp = new AstAdd{fl, newp, selector}; break;
4056 case ARRAY_PRODUCT: newp = new AstMul{fl, newp, selector}; break;
4057 default: nodep->v3fatalSrc("bad case");
4058 }
4059 }
4060 }
4061 nodep->replaceWith(newp);
4062 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4063 } else if (AstCMethodHard* newp = methodCallArray(nodep, adtypep)) {
4064 newp->protect(false);
4065 newp->didWidth(true);
4066 nodep->replaceWith(newp);
4067 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4068 } else {
4069 nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ());
4070 nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
4071 }
4072 }
4073 void methodCallEvent(AstMethodCall* nodep, AstBasicDType*) {
4074 // Method call on event
4075 if (nodep->name() == "triggered") {
4076 methodOkArguments(nodep, 0, 0);
4077 AstCMethodHard* const callp = new AstCMethodHard{
4078 nodep->fileline(), nodep->fromp()->unlinkFrBack(), "isTriggered"};
4079 callp->dtypeSetBit();
4080 nodep->replaceWith(callp);
4081 VL_DO_DANGLING(pushDeletep(nodep), nodep);
4082 } else {
4083 nodep->v3error("Unknown built-in event method " << nodep->prettyNameQ());
4084 }
4085 }
4086 void methodCallString(AstMethodCall* nodep, AstBasicDType*) {
4087 // Method call on string
4088 if (nodep->name() == "len") {
4089 // Constant value
4090 methodOkArguments(nodep, 0, 0);
4091 AstNode* const newp = new AstLenN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
4092 nodep->replaceWith(newp);
4093 VL_DO_DANGLING(pushDeletep(nodep), nodep);
4094 } else if (nodep->name() == "itoa") {
4095 methodOkArguments(nodep, 1, 1);
4096 VL_DO_DANGLING(replaceWithSFormat(nodep, "%0d"), nodep);
4097 } else if (nodep->name() == "hextoa") {
4098 methodOkArguments(nodep, 1, 1);
4099 VL_DO_DANGLING(replaceWithSFormat(nodep, "%0x"), nodep);
4100 } else if (nodep->name() == "octtoa") {
4101 methodOkArguments(nodep, 1, 1);
4102 VL_DO_DANGLING(replaceWithSFormat(nodep, "%0o"), nodep);
4103 } else if (nodep->name() == "bintoa") {
4104 methodOkArguments(nodep, 1, 1);
4105 VL_DO_DANGLING(replaceWithSFormat(nodep, "%0b"), nodep);
4106 } else if (nodep->name() == "realtoa") {
4107 methodOkArguments(nodep, 1, 1);
4108 VL_DO_DANGLING(replaceWithSFormat(nodep, "%g"), nodep);
4109 } else if (nodep->name() == "tolower") {
4110 methodOkArguments(nodep, 0, 0);
4111 AstNode* const newp
4112 = new AstToLowerN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
4113 nodep->replaceWith(newp);
4114 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4115 } else if (nodep->name() == "toupper") {
4116 methodOkArguments(nodep, 0, 0);
4117 AstNode* const newp
4118 = new AstToUpperN{nodep->fileline(), nodep->fromp()->unlinkFrBack()};
4119 nodep->replaceWith(newp);
4120 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4121 } else if (nodep->name() == "compare" || nodep->name() == "icompare") {
4122 const bool ignoreCase = nodep->name()[0] == 'i';
4123 methodOkArguments(nodep, 1, 1);
4124 AstArg* const argp = VN_AS(nodep->pinsp(), Arg);
4125 AstNodeExpr* const lhs = nodep->fromp()->unlinkFrBack();
4126 AstNodeExpr* const rhs = argp->exprp()->unlinkFrBack();
4127 AstNode* const newp = new AstCompareNN{nodep->fileline(), lhs, rhs, ignoreCase};
4128 nodep->replaceWith(newp);
4129 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4130 } else if (nodep->name() == "putc") {
4131 methodOkArguments(nodep, 2, 2);
4132 AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
4133 AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
4134 AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
4135 AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
4136 AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack();
4137 AstVarRef* const varrefp
4138 = new AstVarRef{nodep->fileline(), fromp->varp(), VAccess::READ};
4139 AstNode* const newp = new AstAssign{
4140 nodep->fileline(), fromp, new AstPutcN{nodep->fileline(), varrefp, rhsp, thsp}};
4141 fromp->access(VAccess::WRITE);
4142 pushDeletep(nodep->backp());
4143 VL_DO_DANGLING(nodep->backp()->replaceWith(newp), nodep);
4144 } else if (nodep->name() == "getc") {
4145 methodOkArguments(nodep, 1, 1);
4146 AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
4147 AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack();
4148 AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
4149 AstNodeExpr* const newp = new AstGetcN{nodep->fileline(), lhsp, rhsp};
4150 nodep->replaceWith(newp);
4151 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4152 } else if (nodep->name() == "substr") {
4153 methodOkArguments(nodep, 2, 2);
4154 AstArg* const arg0p = VN_AS(nodep->pinsp(), Arg);
4155 AstArg* const arg1p = VN_AS(arg0p->nextp(), Arg);
4156 AstNodeExpr* const lhsp = nodep->fromp()->unlinkFrBack();
4157 AstNodeExpr* const rhsp = arg0p->exprp()->unlinkFrBack();
4158 AstNodeExpr* const thsp = arg1p->exprp()->unlinkFrBack();
4159 AstNodeExpr* const newp = new AstSubstrN{nodep->fileline(), lhsp, rhsp, thsp};
4160 nodep->replaceWith(newp);
4161 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4162 } else if (nodep->name() == "atobin" || nodep->name() == "atohex"
4163 || nodep->name() == "atoi" || nodep->name() == "atooct"
4164 || nodep->name() == "atoreal") {
4165 AstAtoN::FmtType fmt;
4166 if (nodep->name() == "atobin") {
4167 fmt = AstAtoN::ATOBIN;
4168 } else if (nodep->name() == "atohex") {
4169 fmt = AstAtoN::ATOHEX;
4170 } else if (nodep->name() == "atoi") {
4171 fmt = AstAtoN::ATOI;
4172 } else if (nodep->name() == "atooct") {
4173 fmt = AstAtoN::ATOOCT;
4174 } else if (nodep->name() == "atoreal") {
4175 fmt = AstAtoN::ATOREAL;
4176 } else {
4177 V3ERROR_NA;
4178 fmt = AstAtoN::ATOI;
4179 } // dummy assignment to suppress compiler warning
4180 methodOkArguments(nodep, 0, 0);
4181 AstNode* const newp
4182 = new AstAtoN{nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt};
4183 nodep->replaceWith(newp);
4184 VL_DO_DANGLING(nodep->deleteTree(), nodep);
4185 } else {
4186 nodep->v3error("Unknown built-in string method " << nodep->prettyNameQ());
4187 }
4188 }
4189 AstQueueDType* queueDTypeIndexedBy(AstNodeDType* indexDTypep) {
4190 // Return a Queue data type with the specified index, remembering so can use again if
4191 // needed
4192 if (AstQueueDType* const queuep = m_queueDTypeIndexed[indexDTypep]) {
4193 return queuep;
4194 } else {
4195 auto* const newp = new AstQueueDType{indexDTypep->fileline(), indexDTypep, nullptr};
4196 v3Global.rootp()->typeTablep()->addTypesp(newp);
4197 m_queueDTypeIndexed[indexDTypep] = newp;
4198 return newp;
4199 }
4200 }
4201
4202 void visit(AstNew* nodep) override {
4203 if (nodep->didWidth()) return;
4204 AstClass* classp = nullptr;
4205 bool assign = false;
4206 if (VN_IS(nodep->backp(), Assign)) { // assignment case
4207 assign = true;
4208 AstClassRefDType* const refp
4209 = m_vup ? VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType) : nullptr;
4210 if (!refp) { // e.g. int a = new;
4211 nodep->v3error("new() assignment not legal to non-class data type "
4212 + (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : ""));
4213 nodep->dtypep(m_vup->dtypep());
4214 return;
4215 }
4216 nodep->dtypep(refp);
4217
4218 classp = refp->classp();
4219 UASSERT_OBJ(classp, nodep, "Unlinked");
4220 if (AstNodeFTask* const ftaskp
4221 = VN_CAST(m_memberMap.findMember(classp, "new"), Func)) {
4222 nodep->taskp(ftaskp);
4223 nodep->classOrPackagep(classp);
4224 } else {
4225 // Either made explicitly or V3LinkDot made implicitly
4226 classp->v3fatalSrc("Can't find class's new");
4227 }
4228 if (classp->isVirtual() || classp->isInterfaceClass()) {
4229 nodep->v3error("Illegal to call 'new' using an abstract virtual class "
4230 + AstNode::prettyNameQ(classp->origName())
4231 + " (IEEE 1800-2023 8.21)");
4232 }
4233 } else { // super.new case
4234 // in this case class and taskp() should be properly linked in V3LinkDot.cpp during
4235 // "super" reference resolution
4236 classp = VN_CAST(nodep->classOrPackagep(), Class);
4237 UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()");
4238 UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()");
4239 }
4240 processFTaskRefArgs(nodep);
4241 if (!assign) nodep->dtypeFrom(nodep->taskp());
4242 }
4243 void visit(AstNewCopy* nodep) override {
4244 if (nodep->didWidthAndSet()) return;
4245 AstClassRefDType* const refp = VN_CAST(m_vup->dtypeNullSkipRefp(), ClassRefDType);
4246 if (!refp) { // e.g. int a = new;
4247 nodep->v3error("new() cannot copy from non-class data type "
4248 + (m_vup->dtypeNullp() ? m_vup->dtypep()->prettyDTypeNameQ() : ""));
4249 nodep->dtypep(m_vup->dtypep());
4250 return;
4251 }
4252 nodep->dtypep(refp);
4253 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
4254 if (!similarDTypeRecurse(nodep->dtypep(), nodep->rhsp()->dtypep())) {
4255 nodep->rhsp()->v3error("New-as-copier passed different data type '"
4256 << nodep->dtypep()->prettyTypeName() << "' then expected '"
4257 << nodep->rhsp()->dtypep()->prettyTypeName() << "'");
4258 }
4259 }
4260 void visit(AstNewDynamic* nodep) override {
4261 if (nodep->didWidthAndSet()) return;
4262 AstDynArrayDType* const adtypep = VN_CAST(m_vup->dtypeNullSkipRefp(), DynArrayDType);
4263 if (!adtypep) { // e.g. int a = new;
4264 nodep->v3error(
4265 "dynamic new() not expected in this context (data type must be dynamic array)");
4266 return;
4267 }
4268 // The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it
4269 if (!VN_IS(nodep->backp(), NodeAssign)) {
4270 UINFO(1, "Got backp " << nodep->backp() << endl);
4271 nodep->v3error(
4272 "dynamic new() not expected in this context (expected under an assign)");
4273 return;
4274 }
4275 nodep->dtypep(adtypep);
4276 if (m_vup && m_vup->prelim()) {
4277 iterateCheckSigned32(nodep, "new() size", nodep->sizep(), BOTH);
4278 }
4279 if (nodep->rhsp()) {
4280 iterateCheckTyped(nodep, "Dynamic array new RHS", nodep->rhsp(), adtypep, BOTH);
4281 }
4282 }
4283
4284 void visit(AstPattern* nodep) override {
4285 if (nodep->didWidthAndSet()) return;
4286 UINFO(9, "PATTERN " << nodep << endl);
4287 if (nodep->childDTypep()) { // data_type '{ pattern }
4288 nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
4289 }
4290 if (!nodep->dtypep() && m_vup->dtypeNullp()) { // Get it from parent assignment/pin/etc
4291 nodep->dtypep(m_vup->dtypep());
4292 }
4293 AstNodeDType* dtypep = nodep->dtypep();
4294 if (!dtypep) {
4295 nodep->v3warn(E_UNSUPPORTED, "Unsupported/Illegal: Assignment pattern"
4296 " member not underneath a supported construct: "
4297 << nodep->backp()->prettyTypeName());
4298 return;
4299 }
4300 {
4301 dtypep = dtypep->skipRefp();
4302 nodep->dtypep(dtypep);
4303 UINFO(9, " dtypep " << dtypep << endl);
4304 nodep->dtypep(dtypep);
4305 // Determine replication count, and replicate initial value as
4306 // widths need to be individually determined
4307 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4308 patp = VN_AS(patp->nextp(), PatMember)) {
4309 const int times = visitPatMemberRep(patp);
4310 for (int i = 1; i < times; i++) {
4311 AstPatMember* const newp = patp->cloneTree(false);
4312 patp->addNextHere(newp);
4313 // This loop will see the new elements as part of nextp()
4314 }
4315 }
4316 // Convert any PatMember with multiple items to multiple PatMembers
4317 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4318 patp = VN_AS(patp->nextp(), PatMember)) {
4319 if (patp->lhssp()->nextp()) {
4320 // Can't just addNext, as would add to end of all members.
4321 // So detach, add next and reattach
4322 VNRelinker relinkHandle;
4323 patp->unlinkFrBack(&relinkHandle);
4324 while (AstNodeExpr* const movep = VN_AS(patp->lhssp()->nextp(), NodeExpr)) {
4325 movep->unlinkFrBack(); // Not unlinkFrBackWithNext, just one
4326 AstNode* newkeyp = nullptr;
4327 if (patp->keyp()) newkeyp = patp->keyp()->cloneTree(true);
4328 AstPatMember* const newp
4329 = new AstPatMember{patp->fileline(), movep, newkeyp, nullptr};
4330 patp->addNext(newp);
4331 }
4332 relinkHandle.relink(patp);
4333 }
4334 }
4335 AstPatMember* defaultp = nullptr;
4336 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;) {
4337 AstPatMember* const nextp = VN_AS(patp->nextp(), PatMember);
4338 if (patp->isDefault()) {
4339 if (defaultp) nodep->v3error("Multiple '{ default: } clauses");
4340 defaultp = patp;
4341 patp->unlinkFrBack();
4342 }
4343 patp = nextp;
4344 }
4345 while (const AstConstDType* const vdtypep = VN_CAST(dtypep, ConstDType)) {
4346 dtypep = vdtypep->subDTypep()->skipRefp();
4347 }
4348
4349 userIterate(dtypep, WidthVP{SELF, BOTH}.p());
4350
4351 if (auto* const vdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
4352 VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
4353 } else if (auto* const vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
4354 VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
4355 } else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
4356 VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
4357 } else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) {
4358 VL_DO_DANGLING(patternWildcard(nodep, vdtypep, defaultp), nodep);
4359 } else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) {
4360 VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep);
4361 } else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) {
4362 VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
4363 } else if (VN_IS(dtypep, BasicDType) && VN_AS(dtypep, BasicDType)->isRanged()) {
4364 VL_DO_DANGLING(patternBasic(nodep, dtypep, defaultp), nodep);
4365 } else {
4366 nodep->v3warn(
4367 E_UNSUPPORTED,
4368 "Unsupported: Assignment pattern applies against non struct/union data type: "
4369 << dtypep->prettyDTypeNameQ());
4370 }
4371 }
4372 }
4373 void patternUOrStruct(AstPattern* nodep, AstNodeUOrStructDType* vdtypep,
4374 AstPatMember* defaultp) {
4375 // Due to "default" and tagged patterns, we need to determine
4376 // which member each AstPatMember corresponds to before we can
4377 // determine the dtypep for that PatMember's value, and then
4378 // width the initial value appropriately.
4379 using PatMap = std::map<const AstMemberDType*, AstPatMember*>;
4380 PatMap patmap; // Store member: value
4381 DTypeMap dtypemap; // Store data_type: default_value
4382 {
4383 const AstMemberDType* memp = vdtypep->membersp();
4384 AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember);
4385 while (patp) {
4386 do {
4387 if (patp->keyp()) {
4388 // '{member:value} or '{data_type: default_value}
4389 if (const AstText* textp = VN_CAST(patp->keyp(), Text)) {
4390 // member: value
4391 memp = VN_CAST(m_memberMap.findMember(vdtypep, textp->text()),
4392 MemberDType);
4393 if (!memp) {
4394 patp->keyp()->v3error("Assignment pattern key '"
4395 << textp->text() << "' not found as member");
4396 break;
4397 } else {
4398 const std::pair<PatMap::iterator, bool> ret
4399 = patmap.emplace(memp, patp);
4400 if (!ret.second) {
4401 patp->v3error("Assignment pattern contains duplicate entry: "
4402 << VN_AS(patp->keyp(), Text)->text());
4403 }
4404 memp = VN_AS(memp->nextp(), MemberDType);
4405 }
4406 } else if (AstNodeDType* nodedtypep = VN_CAST(patp->keyp(), NodeDType)) {
4407 // data_type: default_value
4408 userIterate(nodedtypep, WidthVP{SELF, BOTH}.p());
4409 const string dtype = nodedtypep->dtypep()->prettyDTypeName(true);
4410 const auto pair = dtypemap.emplace(dtype, patp);
4411 if (!pair.second) {
4412 // Override stored default_value
4413 pair.first->second = patp->cloneTree(false);
4414 }
4415 } else {
4416 // Undefined pattern
4417 patp->keyp()->v3error(
4418 "Assignment pattern key not supported/understood: "
4419 << patp->keyp()->prettyTypeName());
4420 }
4421 } else {
4422 // constant expr
4423 if (memp) {
4424 const std::pair<PatMap::iterator, bool> ret
4425 = patmap.emplace(memp, patp);
4426 if (!ret.second) {
4427 patp->v3error("Assignment pattern contains duplicate entry: "
4428 << VN_AS(patp->keyp(), Text)->text());
4429 }
4430 memp = VN_AS(memp->nextp(), MemberDType);
4431 }
4432 }
4433 } while (false);
4434
4435 // Next
4436 if (patp) patp = VN_AS(patp->nextp(), PatMember);
4437 }
4438 }
4439 AstNodeExpr* newp = nullptr;
4440 if (vdtypep->packed()) {
4441 for (AstMemberDType* memp = vdtypep->membersp(); memp;
4442 memp = VN_AS(memp->nextp(), MemberDType)) {
4443 const auto it = patmap.find(memp);
4444 AstPatMember* patp = nullptr;
4445 if (it == patmap.end()) { // default or default_type assignment
4446 if (AstNodeUOrStructDType* const memp_nested_vdtypep
4447 = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) {
4448 newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp,
4449 newp, nodep, dtypemap);
4450 } else {
4451 patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp,
4452 dtypemap);
4453 newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep);
4454 }
4455 } else { // member assignment
4456 patp = it->second;
4457 newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep);
4458 }
4459 }
4460 } else { // Unpacked
4461 AstConsPackMember* membersp = nullptr;
4462 for (AstMemberDType* memp = vdtypep->membersp(); memp;
4463 memp = VN_AS(memp->nextp(), MemberDType)) {
4464 const auto it = patmap.find(memp);
4465 AstPatMember* patp = nullptr;
4466 if (it == patmap.end()) { // Default or default_type assignment
4467 patp = defaultPatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp,
4468 dtypemap);
4469 } else {
4470 patp = it->second; // Member assignment
4471 }
4472 patp->dtypep(memp);
4473 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4474 AstConsPackMember* const cpmp
4475 = new AstConsPackMember{patp->fileline(), memp, valuep};
4476 membersp = membersp ? membersp->addNext(cpmp) : cpmp;
4477 }
4478 newp = new AstConsPackUOrStruct{nodep->fileline(), vdtypep, membersp};
4479 }
4480 if (newp) {
4481 nodep->replaceWith(newp);
4482 } else {
4483 nodep->v3error("Assignment pattern with no members");
4484 }
4485 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4486 }
4487
4488 AstNodeExpr* nestedvalueConcat_patternUOrStruct(AstNodeUOrStructDType* memp_vdtypep,
4489 AstPatMember* defaultp, AstNodeExpr* newp,
4490 AstPattern* nodep, const DTypeMap& dtypemap) {
4491 AstPatMember* patp = nullptr;
4492 for (AstMemberDType* memp_nested = memp_vdtypep->membersp(); memp_nested;
4493 memp_nested = VN_AS(memp_nested->nextp(), MemberDType)) {
4494 if (AstNodeUOrStructDType* const memp_multinested_vdtypep
4495 = VN_CAST(memp_nested->virtRefDTypep(), NodeUOrStructDType)) {
4496 // When unpacked struct/union is supported this if will need some additional
4497 // conditions
4498 newp = nestedvalueConcat_patternUOrStruct(memp_multinested_vdtypep, defaultp, newp,
4499 nodep, dtypemap);
4500 } else {
4501 patp = defaultPatp_patternUOrStruct(nodep, memp_nested, patp, memp_vdtypep,
4502 defaultp, dtypemap);
4503 newp = valueConcat_patternUOrStruct(patp, newp, memp_nested, nodep);
4504 }
4505 }
4506 return newp;
4507 }
4508
4509 AstPatMember* defaultPatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp,
4510 AstPatMember* patp,
4511 AstNodeUOrStructDType* memp_vdtypep,
4512 AstPatMember* defaultp, const DTypeMap& dtypemap) {
4513 const string memp_DType = memp->virtRefDTypep()->prettyDTypeName(true);
4514 const auto it = dtypemap.find(memp_DType);
4515 if (it != dtypemap.end()) {
4516 // default_value for data_type
4517 patp = it->second->cloneTree(false);
4518 } else if (defaultp) {
4519 // default_value for any unmatched member yet
4520 patp = defaultp->cloneTree(false);
4521 } else {
4522 if (!VN_IS(memp_vdtypep, UnionDType)) {
4523 nodep->v3error("Assignment pattern missed initializing elements: "
4524 << memp->virtRefDTypep()->prettyDTypeNameQ() << " "
4525 << memp->prettyNameQ());
4526 }
4527 }
4528 return patp;
4529 }
4530
4531 AstNodeExpr* valueConcat_patternUOrStruct(AstPatMember* patp, AstNodeExpr* newp,
4532 AstMemberDType* memp, AstPattern* nodep) {
4533 if (patp) {
4534 patp->dtypep(memp);
4535 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4536 if (!newp) {
4537 newp = valuep;
4538 } else {
4539 AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep};
4540 newp = concatp;
4541 newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(),
4542 nodep->dtypep()->numeric());
4543 }
4544 }
4545 return newp;
4546 }
4547
4548 void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
4549 const VNumRange range = arrayDtp->declRange();
4550 PatVecMap patmap = patVectorMap(nodep, range);
4551 UINFO(9, "ent " << range.left() << " to " << range.right() << endl);
4552 AstNode* newp = nullptr;
4553 bool allConstant = true;
4554 const bool isConcat = nodep->itemsp() && VN_AS(nodep->itemsp(), PatMember)->isConcat();
4555 for (int entn = 0, ent = range.left(); entn < range.elements();
4556 ++entn, ent += range.leftToRightInc()) {
4557 AstPatMember* newpatp = nullptr;
4558 AstPatMember* patp = nullptr;
4559 const auto it = patmap.find(ent);
4560 if (it == patmap.end()) {
4561 if (defaultp) {
4562 newpatp = defaultp->cloneTree(false);
4563 patp = newpatp;
4564 } else if (!(VN_IS(arrayDtp, UnpackArrayDType) && !allConstant && isConcat)) {
4565 // If arrayDtp is an unpacked array and item is not constant,
4566 // the number of elements cannot be determined here as the dtype of each
4567 // element is not set yet. V3Slice checks for such cases.
4568 nodep->v3error("Assignment pattern missed initializing elements: " << ent);
4569 }
4570 } else {
4571 patp = it->second;
4572 patmap.erase(it);
4573 }
4574
4575 if (patp) {
4576 // Don't want the RHS an array
4577 allConstant &= VN_IS(patp->lhssp(), Const);
4578 patp->dtypep(arrayDtp->subDTypep());
4579 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4580 if (VN_IS(arrayDtp, UnpackArrayDType)) {
4581 if (!newp) {
4582 AstInitArray* const newap
4583 = new AstInitArray{nodep->fileline(), arrayDtp, nullptr};
4584 newp = newap;
4585 }
4586 VN_AS(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
4587 } else { // Packed. Convert to concat for now.
4588 if (!newp) {
4589 newp = valuep;
4590 } else {
4591 AstConcat* const concatp
4592 = new AstConcat{patp->fileline(), VN_AS(newp, NodeExpr), valuep};
4593 newp = concatp;
4594 newp->dtypeSetLogicSized(concatp->lhsp()->width()
4595 + concatp->rhsp()->width(),
4596 nodep->dtypep()->numeric());
4597 }
4598 }
4599 }
4600 if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
4601 }
4602 if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
4603 if (newp) {
4604 nodep->replaceWith(newp);
4605 } else {
4606 nodep->v3error("Assignment pattern with no members");
4607 }
4608 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4609 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4610 }
4611 void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) {
4612 AstNode* defaultValuep = nullptr;
4613 if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
4614 AstNodeExpr* newp = new AstConsAssoc{nodep->fileline(), defaultValuep};
4615 newp->dtypeFrom(arrayDtp);
4616 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4617 patp = VN_AS(patp->nextp(), PatMember)) {
4618 patp->dtypep(arrayDtp->subDTypep());
4619 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4620 AstNode* keyp = patp->keyp();
4621 if (!keyp) {
4622 patp->v3error("Missing pattern key (need an expression then a ':')");
4623 keyp = new AstConst{nodep->fileline(), AstConst::Signed32{}, 0};
4624 } else {
4625 keyp->unlinkFrBack();
4626 }
4627 AstSetAssoc* const newap = new AstSetAssoc{nodep->fileline(), newp, keyp, valuep};
4628 newap->dtypeFrom(arrayDtp);
4629 newp = newap;
4630 }
4631 nodep->replaceWith(newp);
4632 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4633 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4634 }
4635 void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp,
4636 AstPatMember* defaultp) {
4637 AstNode* defaultValuep = nullptr;
4638 if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
4639 AstNode* newp = new AstConsWildcard{nodep->fileline(), defaultValuep};
4640 newp->dtypeFrom(arrayDtp);
4641 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4642 patp = VN_AS(patp->nextp(), PatMember)) {
4643 patp->dtypep(arrayDtp->subDTypep());
4644 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4645 AstNode* const keyp = patp->keyp();
4646 AstSetWildcard* const newap
4647 = new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep};
4648 newap->dtypeFrom(arrayDtp);
4649 newp = newap;
4650 }
4651 nodep->replaceWith(newp);
4652 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4653 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4654 }
4655 void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) {
4656 AstNode* newp = new AstConsDynArray{nodep->fileline()};
4657 newp->dtypeFrom(arrayp);
4658 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4659 patp = VN_AS(patp->nextp(), PatMember)) {
4660 patp->dtypep(arrayp->subDTypep());
4661 AstNodeExpr* const rhsp = patternMemberValueIterate(patp);
4662 const bool rhsIsValue
4663 = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr)
4664 .isAssignable();
4665 AstConsDynArray* const newap
4666 = new AstConsDynArray{nodep->fileline(), rhsIsValue, rhsp, false, newp};
4667 newap->dtypeFrom(arrayp);
4668 newp = newap;
4669 }
4670 nodep->replaceWith(newp);
4671 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4672 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4673 }
4674 void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember*) {
4675 AstNode* newp = new AstConsQueue{nodep->fileline()};
4676 newp->dtypeFrom(arrayp);
4677 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
4678 patp = VN_AS(patp->nextp(), PatMember)) {
4679 patp->dtypep(arrayp->subDTypep());
4680 AstNodeExpr* const rhsp = patternMemberValueIterate(patp);
4681 const bool rhsIsValue
4682 = AstNode::computeCastable(rhsp->dtypep(), arrayp->subDTypep(), nullptr)
4683 .isAssignable();
4684 AstConsQueue* const newap
4685 = new AstConsQueue{nodep->fileline(), rhsIsValue, rhsp, false, newp};
4686 newap->dtypeFrom(arrayp);
4687 newp = newap;
4688 }
4689 nodep->replaceWith(newp);
4690 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4691 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4692 }
4693 void patternBasic(AstPattern* nodep, AstNodeDType* vdtypep, AstPatMember* defaultp) {
4694 const AstBasicDType* bdtypep = VN_AS(vdtypep, BasicDType);
4695 const VNumRange range = bdtypep->declRange();
4696 PatVecMap patmap = patVectorMap(nodep, range);
4697 UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
4698 AstNodeExpr* newp = nullptr;
4699 for (int ent = range.hi(); ent >= range.lo(); --ent) {
4700 AstPatMember* newpatp = nullptr;
4701 AstPatMember* patp = nullptr;
4702 const auto it = patmap.find(ent);
4703 if (it == patmap.end()) {
4704 if (defaultp) {
4705 newpatp = defaultp->cloneTree(false);
4706 patp = newpatp;
4707 } else {
4708 nodep->v3error("Assignment pattern missed initializing elements: " << ent);
4709 }
4710 } else {
4711 patp = it->second;
4712 patmap.erase(it);
4713 }
4714 if (patp) {
4715 // Determine initial values
4716 vdtypep = nodep->findBitDType();
4717 patp->dtypep(vdtypep);
4718 AstNodeExpr* const valuep = patternMemberValueIterate(patp);
4719 { // Packed. Convert to concat for now.
4720 if (!newp) {
4721 newp = valuep;
4722 } else {
4723 AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep};
4724 newp = concatp;
4725 newp->dtypeSetLogicSized(concatp->lhsp()->width()
4726 + concatp->rhsp()->width(),
4727 nodep->dtypep()->numeric());
4728 }
4729 }
4730 }
4731 if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp);
4732 }
4733 if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements");
4734 if (newp) {
4735 nodep->replaceWith(newp);
4736 } else {
4737 nodep->v3error("Assignment pattern with no members");
4738 }
4739 // if (debug() >= 9) newp->dumpTree("- apat-out: ");
4740 VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
4741 }
4742 AstNodeExpr* patternMemberValueIterate(AstPatMember* patp) {
4743 // Determine values - might be another InitArray
4744 userIterate(patp, WidthVP{patp->dtypep(), BOTH}.p());
4745 // Convert to InitArray or constify immediately
4746 AstNodeExpr* valuep = patp->lhssp()->unlinkFrBack();
4747 if (VN_IS(valuep, Const)) {
4748 // Forming a AstConcat will cause problems with
4749 // unsized (uncommitted sized) constants
4750 if (AstConst* const newp = V3WidthCommit::newIfConstCommitSize(VN_AS(valuep, Const))) {
4751 VL_DO_DANGLING(pushDeletep(valuep), valuep);
4752 valuep = newp;
4753 }
4754 }
4755 return valuep;
4756 }
4757
4758 static void checkEventAssignment(const AstNodeAssign* const asgnp) {
4759 string unsupEvtAsgn;
4760 if (!usesDynamicScheduler(asgnp->lhsp())) unsupEvtAsgn = "to";
4761 if (asgnp->rhsp()->dtypep()->isEvent() && !usesDynamicScheduler(asgnp->rhsp())) {
4762 unsupEvtAsgn += (unsupEvtAsgn.empty() ? "from" : " and from");
4763 }
4764 if (!unsupEvtAsgn.empty()) {
4765 asgnp->v3warn(E_UNSUPPORTED, "Assignment "
4766 << unsupEvtAsgn
4767 << " event in statically scheduled context.\n"
4768 << asgnp->warnMore()
4769 << "Static event "
4770 "scheduling won't be able to handle this.\n"
4771 << asgnp->warnMore()
4772 << "... Suggest move the event into a "
4773 "completely dynamic context, eg. a class, and "
4774 "reference it only from such context.");
4775 }
4776 }
4777
4778 static bool usesDynamicScheduler(AstNode* nodep) {
4779 UASSERT_OBJ(nodep->dtypep()->isEvent(), nodep, "Node does not have an event dtype");
4780 while (true) {
4781 AstVarRef* const vrefp = VN_CAST(nodep, VarRef);
4782 if (vrefp) return usesDynamicScheduler(vrefp);
4783 if (VN_IS(nodep, MemberSel)) {
4784 return true;
4785 } else if (AstNodeSel* selp = VN_CAST(nodep, NodeSel)) {
4786 nodep = selp->fromp();
4787 } else {
4788 return false;
4789 }
4790 }
4791 }
4792
4793 static bool usesDynamicScheduler(AstVarRef* vrefp) {
4794 return VN_IS(vrefp->classOrPackagep(), Class) || vrefp->varp()->isFuncLocal();
4795 }
4796
4797 void visit(AstPatMember* nodep) override {
4798 AstNodeDType* const vdtypep = m_vup->dtypeNullp();
4799 UASSERT_OBJ(vdtypep, nodep, "Pattern member type not assigned by AstPattern visitor");
4800 nodep->dtypep(vdtypep);
4801 UINFO(9, " PATMEMBER " << nodep << endl);
4802 UASSERT_OBJ(!nodep->lhssp()->nextp(), nodep,
4803 "PatMember value should be singular w/replicates removed");
4804 // Need to propagate assignment type downwards, even on prelim
4805 userIterateChildren(nodep, WidthVP{nodep->dtypep(), PRELIM}.p());
4806 iterateCheck(nodep, "Pattern value", nodep->lhssp(), ASSIGN, FINAL, vdtypep, EXTEND_LHS);
4807 }
4808 int visitPatMemberRep(AstPatMember* nodep) {
4809 uint32_t times = 1;
4810 if (nodep->repp()) { // else repp()==nullptr shorthand for rep count 1
4811 iterateCheckSizedSelf(nodep, "LHS", nodep->repp(), SELF, BOTH);
4812 V3Const::constifyParamsEdit(nodep->repp()); // repp may change
4813 const AstConst* const constp = VN_CAST(nodep->repp(), Const);
4814 if (!constp) {
4815 nodep->v3error("Replication value isn't a constant.");
4816 times = 0;
4817 } else {
4818 times = constp->toUInt();
4819 }
4820 if (times == 0) {
4821 nodep->v3error("Pattern replication value of 0 is not legal.");
4822 times = 1;
4823 }
4824 nodep->repp()
4825 ->unlinkFrBackWithNext()
4826 ->deleteTree(); // Done with replicate before cloning
4827 }
4828 return times;
4829 }
4830
4831 void visit(AstPropSpec* nodep) override {
4832 if (m_vup->prelim()) { // First stage evaluation
4833 iterateCheckBool(nodep, "Property", nodep->propp(), BOTH);
4834 userIterateAndNext(nodep->sensesp(), nullptr);
4835 if (nodep->disablep()) {
4836 iterateCheckBool(nodep, "Disable", nodep->disablep(),
4837 BOTH); // it's like an if() condition.
4838 }
4839 nodep->dtypeSetBit();
4840 }
4841 }
4842
4843 //--------------------
4844 // Top levels
4845
4846 void visit(AstNodeCase* nodep) override {
4847 // IEEE-2012 12.5:
4848 // Width: MAX(expr, all items)
4849 // Signed: Only if expr, and all items signed
4850 assertAtStatement(nodep);
4851 userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
4852 for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
4853 nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced
4854 if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->stmtsp(), nullptr);
4855 for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
4856 nextcp = condp->nextp(); // Prelim may cause the node to get replaced
4857 VL_DO_DANGLING(userIterate(condp, WidthVP{CONTEXT_DET, PRELIM}.p()), condp);
4858 }
4859 }
4860
4861 // Deal with case(type(data_type))
4862 if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) {
4863 if (exprap->attrType() == VAttrType::TYPEID) {
4864 AstNodeDType* const exprDtp = VN_AS(exprap->fromp(), NodeDType);
4865 UINFO(9, "case type exprDtp " << exprDtp << endl);
4866 // V3Param may have a pointer to this case statement, and we need
4867 // dotted references to remain properly named, so rather than
4868 // removing we convert it to a "normal" expression "case (1) ..."
4869 FileLine* const newfl = nodep->fileline();
4870 newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform
4871 newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform
4872 nodep->fileline(newfl);
4873 for (AstCaseItem* itemp = nodep->itemsp(); itemp;
4874 itemp = VN_AS(itemp->nextp(), CaseItem)) {
4875 if (!itemp->isDefault()) {
4876 bool hit = false;
4877 for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
4878 const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf);
4879 if (!condAttrp) {
4880 condp->v3error(
4881 "Case(type) statement requires items that have type() items");
4882 } else {
4883 AstNodeDType* const condDtp = VN_AS(condAttrp->fromp(), NodeDType);
4884 if (AstNode::computeCastable(exprDtp, condDtp, nodep)
4885 == VCastable::SAMEISH) {
4886 hit = true;
4887 break;
4888 }
4889 }
4890 }
4891 pushDeletep(itemp->condsp()->unlinkFrBackWithNext());
4892 // Item condition becomes constant 1 if hits else 0
4893 itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit});
4894 }
4895 }
4896 VL_DO_DANGLING(pushDeletep(exprap->unlinkFrBack()), exprap);
4897 nodep->exprp(new AstConst{newfl, AstConst::BitTrue{}});
4898 }
4899 }
4900
4901 // Take width as maximum across all items, if any is real whole thing is real
4902 AstNodeDType* subDTypep = nodep->exprp()->dtypep();
4903 for (AstCaseItem* itemp = nodep->itemsp(); itemp;
4904 itemp = VN_AS(itemp->nextp(), CaseItem)) {
4905 for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) {
4906 if (condp->dtypep() != subDTypep) {
4907 if (condp->dtypep()->isDouble() || subDTypep->isDouble()) {
4908 subDTypep = nodep->findDoubleDType();
4909 } else if (condp->dtypep()->isString() || subDTypep->isString()) {
4910 subDTypep = nodep->findStringDType();
4911 } else {
4912 const int width = std::max(subDTypep->width(), condp->width());
4913 const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin());
4914 const bool issigned = subDTypep->isSigned() && condp->isSigned();
4915 subDTypep
4916 = nodep->findLogicDType(width, mwidth, VSigning::fromBool(issigned));
4917 }
4918 }
4919 }
4920 }
4921 // Apply width
4922 iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep,
4923 EXTEND_LHS);
4924 for (AstCaseItem* itemp = nodep->itemsp(); itemp;
4925 itemp = VN_AS(itemp->nextp(), CaseItem)) {
4926 for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
4927 nextcp = condp->nextp(); // Final may cause the node to get replaced
4928 iterateCheck(nodep, "Case Item", condp, CONTEXT_DET, FINAL, subDTypep, EXTEND_LHS);
4929 }
4930 }
4931 }
4932 void visit(AstRandCase* nodep) override {
4933 // IEEE says each item is a int (32-bits), and sizes are based on natural sizing,
4934 // but we'll sum to a 64-bit number then math is faster.
4935 assertAtStatement(nodep);
4936 v3Global.useRandomizeMethods(true);
4937 AstNodeDType* const itemDTypep = nodep->findUInt32DType();
4938 for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
4939 nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced
4940 userIterateAndNext(itemp->stmtsp(), nullptr);
4941 for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) {
4942 nextcp = condp->nextp(); // Prelim may cause the node to get replaced
4943 iterateCheckTyped(itemp, "Randcase Item", condp, itemDTypep, BOTH);
4944 VL_DANGLING(condp); // Might have been replaced
4945 }
4946 VL_DANGLING(itemp); // Might have been replaced
4947 }
4948 }
4949
4950 void visit(AstNodeFor* nodep) override {
4951 assertAtStatement(nodep);
4952 userIterateAndNext(nodep->initsp(), nullptr);
4953 iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
4954 BOTH); // it's like an if() condition.
4955 if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->stmtsp(), nullptr);
4956 userIterateAndNext(nodep->incsp(), nullptr);
4957 }
4958 void visit(AstRepeat* nodep) override {
4959 assertAtStatement(nodep);
4960 userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
4961 userIterateAndNext(nodep->stmtsp(), nullptr);
4962 }
4963 void visit(AstWhile* nodep) override {
4964 assertAtStatement(nodep);
4965 userIterateAndNext(nodep->precondsp(), nullptr);
4966 iterateCheckBool(nodep, "For Test Condition", nodep->condp(),
4967 BOTH); // it's like an if() condition.
4968 userIterateAndNext(nodep->stmtsp(), nullptr);
4969 userIterateAndNext(nodep->incsp(), nullptr);
4970 }
4971 270 void visit(AstNodeIf* nodep) override {
4972 270 assertAtStatement(nodep);
4973 // if (debug()) nodep->dumpTree("- IfPre: ");
4974 if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly
4975 270 userIterateAndNext(nodep->thensp(), nullptr);
4976 270 userIterateAndNext(nodep->elsesp(), nullptr);
4977 }
4978 270 iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition.
4979 // if (debug()) nodep->dumpTree("- IfOut: ");
4980 270 }
4981 void visit(AstExprStmt* nodep) override {
4982 userIterateAndNext(nodep->stmtsp(), nullptr);
4983 // expected result is same as parent's expected result
4984 userIterateAndNext(nodep->resultp(), m_vup);
4985 nodep->dtypeFrom(nodep->resultp());
4986 }
4987 void visit(AstNodeForeach* nodep) override {
4988 if (nodep->didWidth()) return;
4989 nodep->didWidth(true);
4990 const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
4991 UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
4992 // if (debug()) nodep->dumpTree("- foreach-old: ");
4993 userIterateAndNext(loopsp->fromp(), WidthVP{SELF, BOTH}.p());
4994 AstNodeExpr* const fromp = loopsp->fromp();
4995 UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
4996 AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
4997 // Major dimension first
4998 for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
4999 next_argsp = argsp->nextp();
5000 const bool empty = VN_IS(argsp, Empty);
5001 AstVar* const varp = VN_CAST(argsp, Var);
5002 UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
5003 if (varp) varp->usedLoopIdx(true);
5004 if (!fromDtp) {
5005 argsp->v3error("foreach loop variables exceed number of indices of array");
5006 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
5007 return;
5008 }
5009 fromDtp = fromDtp->skipRefp();
5010 UINFO(9, "- foreachArg " << argsp << endl);
5011 UINFO(9, "- from on " << fromp << endl);
5012 UINFO(9, "- from dtp " << fromDtp << endl);
5013
5014 if (VN_IS(fromDtp, NodeArrayDType) || VN_IS(fromDtp, DynArrayDType)
5015 || VN_IS(fromDtp, QueueDType)) {
5016 // Nothing special here
5017 } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
5018 if (!adtypep->isString() && !adtypep->isRanged()) {
5019 argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
5020 << " data type");
5021 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
5022 return;
5023 }
5024 } else if (const AstAssocArrayDType* const adtypep
5025 = VN_CAST(fromDtp, AssocArrayDType)) {
5026 varp->dtypeFrom(adtypep->keyDTypep());
5027 } else {
5028 argsp->v3error("Illegal 'foreach' loop on " << fromDtp->prettyTypeName()
5029 << " data type");
5030 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
5031 return;
5032 }
5033 fromDtp = fromDtp->subDTypep();
5034 }
5035 // The parser validates we don't have "foreach (array[,,,])"
5036 AstNode* const bodyp = nodep->stmtsp();
5037 userIterateAndNext(bodyp, nullptr);
5038 if (AstForeach* const loopp = VN_CAST(nodep, Foreach)) {
5039 VL_DO_DANGLING(V3Begin::convertToWhile(loopp), nodep);
5040 return;
5041 }
5042 }
5043
5044 3735 void visit(AstNodeAssign* nodep) override {
5045 // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is
5046 // only one step; final dtype depends on assign LHS.)
5047 // Determine RHS type width and signing
5048 // Propagate type down to *non-self-determined* operators
5049 // Real propagates only across one operator if one side is real -
5050 // handled in each visitor.
5051 // Then LHS sign-extends only if *RHS* is signed
5052 3735 assertAtStatement(nodep);
5053 // if (debug()) nodep->dumpTree("- AssignPre: ");
5054 {
5055 // if (debug()) nodep->dumpTree("- assin:: ");
5056
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3735 times.
3735 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5057
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3735 times.
3735 UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LHS be untyped?");
5058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3735 times.
3735 UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LHS be unsized?");
5059
1/2
✓ Branch 0 taken 3735 times.
✗ Branch 1 not taken.
3735 nodep->dtypeFrom(nodep->lhsp());
5060 //
5061 // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim
5062 // if (debug()) nodep->dumpTree("- assrhs: ");
5063 3735 userIterateAndNext(nodep->rhsp(), WidthVP{nodep->dtypep(), PRELIM}.p());
5064 //
5065 // if (debug()) nodep->dumpTree("- assign: ");
5066 AstNodeDType* const lhsDTypep
5067 = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined
5068 3735 iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
5069 // if (debug()) nodep->dumpTree("- AssignOut: ");
5070 }
5071
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3735 times.
3735 if (auto* const controlp = nodep->timingControlp()) {
5072 if (VN_IS(m_ftaskp, Func)) {
5073 controlp->v3error("Timing controls are not legal in functions. Suggest use a task "
5074 "(IEEE 1800-2023 13.4.4)");
5075 VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
5076 } else if (nodep->fileline()->timingOn() && v3Global.opt.timing().isSetTrue()) {
5077 iterateNull(controlp);
5078 } else {
5079 if (nodep->fileline()->timingOn()) {
5080 if (v3Global.opt.timing().isSetFalse()) {
5081 controlp->v3warn(ASSIGNDLY, "Ignoring timing control on this "
5082 "assignment/primitive due to --no-timing");
5083 } else {
5084 controlp->v3warn(E_NEEDTIMINGOPT,
5085 "Use --timing or --no-timing to specify how "
5086 "timing controls should be handled");
5087 }
5088 }
5089 VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
5090 }
5091 }
5092 if (VN_IS(nodep->rhsp(), EmptyQueue)) {
5093 UINFO(9, "= {} -> .delete(): " << nodep);
5094 const AstNodeDType* const lhsDtp = nodep->lhsp()->dtypep()->skipRefp();
5095 if (!VN_IS(lhsDtp, QueueDType) && !VN_IS(lhsDtp, DynArrayDType)) {
5096 nodep->v3warn(E_UNSUPPORTED,
5097 "Unsupported/Illegal: empty queue ('{}') in this assign context");
5098 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
5099 return;
5100 }
5101 AstMethodCall* const newp = new AstMethodCall{
5102 nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete", nullptr};
5103 newp->dtypeSetVoid();
5104 nodep->replaceWith(newp->makeStmt());
5105 VL_DO_DANGLING(pushDeletep(nodep), nodep);
5106 // Need to now convert it
5107 visit(newp);
5108 return;
5109 }
5110 if (const AstNewDynamic* const dynp = VN_CAST(nodep->rhsp(), NewDynamic)) {
5111 UINFO(9, "= new[] -> .resize(): " << nodep);
5112 AstCMethodHard* newp;
5113 if (!dynp->rhsp()) {
5114 newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
5115 "renew", dynp->sizep()->unlinkFrBack()};
5116 } else {
5117 newp = new AstCMethodHard{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
5118 "renew_copy", dynp->sizep()->unlinkFrBack()};
5119 newp->addPinsp(dynp->rhsp()->unlinkFrBack());
5120 }
5121 newp->didWidth(true);
5122 newp->protect(false);
5123 newp->dtypeSetVoid();
5124 nodep->replaceWith(newp->makeStmt());
5125 VL_DO_DANGLING(pushDeletep(nodep), nodep);
5126 return;
5127 }
5128
5129 // Width check for unpacked array stream assignment
5130 if (const AstNodeStream* streamp = VN_CAST(nodep->rhsp(), NodeStream)) {
5131 if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) {
5132 int lwidth = nodep->lhsp()->width();
5133 int rwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements();
5134 if (lwidth != 0 && lwidth < rwidth) {
5135 nodep->v3widthWarn(lwidth, rwidth,
5136 "Target fixed size variable ("
5137 << lwidth << " bits) is narrower than the stream ("
5138 << rwidth << " bits) (IEEE 1800-2023 11.4.14)");
5139 }
5140 }
5141 } else if (const AstNodeStream* streamp = VN_CAST(nodep->lhsp(), NodeStream)) {
5142 if (AstUnpackArrayDType* arr = VN_CAST(streamp->lhsp()->dtypep(), UnpackArrayDType)) {
5143 int rwidth = nodep->rhsp()->width();
5144 int lwidth = arr->subDTypep()->width() * arr->arrayUnpackedElements();
5145 if (rwidth != 0 && rwidth < lwidth) {
5146 nodep->v3widthWarn(lwidth, rwidth,
5147 "Stream target requires "
5148 << lwidth
5149 << " bits, but source expression only provides "
5150 << rwidth << " bits (IEEE 1800-2023 11.4.14.3)");
5151 }
5152 }
5153 }
5154
5155
2/4
✓ Branch 1 taken 3735 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3735 times.
3735 if (nodep->hasDType() && nodep->dtypep()->isEvent()) {
5156 checkEventAssignment(nodep);
5157 v3Global.setAssignsEvents();
5158 }
5159 }
5160
5161 void visit(AstRelease* nodep) override {
5162 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5163 UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?");
5164 UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?");
5165 }
5166
5167 void formatNoStringArg(AstNode* argp, char ch) {
5168 if (argp && argp->isString()) {
5169 argp->v3error("$display-line format of '%"s + ch + "' illegal with string argument\n"
5170 << argp->warnMore() << "... Suggest use '%s'");
5171 }
5172 }
5173
5174 void visit(AstSFormat* nodep) override {
5175 assertAtStatement(nodep);
5176 userIterateAndNext(nodep->fmtp(), WidthVP{SELF, BOTH}.p());
5177 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5178 }
5179 void visit(AstSFormatF* nodep) override {
5180 // Excludes NodeDisplay, see below
5181 if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function
5182 // Just let all arguments seek their natural sizes
5183 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5184 //
5185 UINFO(9, " Display in " << nodep->text() << endl);
5186 string newFormat;
5187 bool inPct = false;
5188 AstNodeExpr* argp = nodep->exprsp();
5189 const string txt = nodep->text();
5190 string fmt;
5191 for (char ch : txt) {
5192 if (!inPct && ch == '%') {
5193 inPct = true;
5194 fmt = ch;
5195 } else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
5196 fmt += ch;
5197 } else if (inPct) {
5198 inPct = false;
5199 bool added = false;
5200 const AstNodeDType* const dtypep = argp ? argp->dtypep()->skipRefp() : nullptr;
5201 const AstBasicDType* const basicp = dtypep ? dtypep->basicp() : nullptr;
5202 ch = std::tolower(ch);
5203 if (ch == '?') { // Unspecified by user, guess
5204 if (argp && argp->isDouble()) {
5205 ch = 'g';
5206 } else if (argp && argp->isString()) {
5207 ch = '@';
5208 } else if (argp && nodep->missingArgChar() == 'd' && argp->isSigned()) {
5209 ch = '~';
5210 } else if (basicp) {
5211 ch = nodep->missingArgChar();
5212 } else {
5213 ch = 'p';
5214 }
5215 }
5216 switch (ch) {
5217 case '%': break; // %% - just output a %
5218 case 'm': break; // %m - auto insert "name"
5219 case 'l': break; // %m - auto insert "library"
5220 case 'd': { // Convert decimal to either 'd' or '#'
5221 if (argp) {
5222 AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr);
5223 formatNoStringArg(argp, ch);
5224 if (argp->isDouble()) {
5225 spliceCvtS(argp, true, 64);
5226 ch = '~';
5227 } else if (argp->isSigned()) { // Convert it
5228 ch = '~';
5229 }
5230 argp = nextp;
5231 }
5232 break;
5233 }
5234 case 'b': // FALLTHRU
5235 case 'o': // FALLTHRU
5236 case 'x': {
5237 if (argp) {
5238 AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr);
5239 formatNoStringArg(argp, ch);
5240 if (argp->isDouble()) spliceCvtS(argp, true, 64);
5241 argp = nextp;
5242 }
5243 break;
5244 }
5245 case 'p': { // Pattern
5246 if (basicp && basicp->isString()) {
5247 added = true;
5248 newFormat += "\"%@\"";
5249 } else if (basicp && basicp->isDouble()) {
5250 added = true;
5251 newFormat += "%g";
5252 } else if (VN_IS(dtypep, AssocArrayDType) //
5253 || VN_IS(dtypep, WildcardArrayDType) //
5254 || VN_IS(dtypep, ClassRefDType) //
5255 || VN_IS(dtypep, DynArrayDType) //
5256 || VN_IS(dtypep, UnpackArrayDType) //
5257 || VN_IS(dtypep, QueueDType)
5258 || (VN_IS(dtypep, NodeUOrStructDType)
5259 && !VN_AS(dtypep, NodeUOrStructDType)->packed())) {
5260 added = true;
5261 newFormat += "%@";
5262 VNRelinker handle;
5263 argp->unlinkFrBack(&handle);
5264 FileLine* const flp = nodep->fileline();
5265 AstCExpr* const newp = new AstCExpr{flp, nullptr};
5266 newp->addExprsp(new AstText{flp, "VL_TO_STRING(", true});
5267 newp->addExprsp(argp);
5268 newp->addExprsp(new AstText{flp, ")", true});
5269 newp->dtypeSetString();
5270 newp->pure(true);
5271 newp->protect(false);
5272 handle.relink(newp);
5273 // Set argp to what we replaced it with, as we will keep processing the
5274 // next argument.
5275 argp = newp;
5276 } else {
5277 added = true;
5278 if (fmt == "%0") {
5279 newFormat += "'h%0h"; // IEEE our choice
5280 } else {
5281 newFormat += "%d";
5282 }
5283 }
5284 if (argp) argp = VN_AS(argp->nextp(), NodeExpr);
5285 break;
5286 }
5287 case 's': { // Convert string to pack string
5288 if (argp && argp->dtypep()->basicp()->isString()) { // Convert it
5289 ch = '@';
5290 }
5291 if (argp) argp = VN_AS(argp->nextp(), NodeExpr);
5292 break;
5293 }
5294 case 't': { // Convert decimal time to realtime
5295 if (argp) {
5296 AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr);
5297 formatNoStringArg(argp, ch);
5298 if (argp->isDouble()) ch = '^'; // Convert it
5299 UASSERT_OBJ(!nodep->timeunit().isNone(), nodep,
5300 "display %t has no time units");
5301 argp = nextp;
5302 }
5303 break;
5304 }
5305 case 'e': // FALLTHRU
5306 case 'f': // FALLTHRU
5307 case 'g': {
5308 if (argp) {
5309 AstNodeExpr* const nextp = VN_AS(argp->nextp(), NodeExpr);
5310 formatNoStringArg(argp, ch);
5311 if (!argp->isDouble()) {
5312 iterateCheckReal(nodep, "Display argument", argp, BOTH);
5313 }
5314 argp = nextp;
5315 }
5316 break;
5317 }
5318 default: { // Most operators, just move to next argument
5319 if (argp) argp = VN_AS(argp->nextp(), NodeExpr);
5320 break;
5321 }
5322 } // switch
5323 if (!added) {
5324 fmt += ch;
5325 newFormat += fmt;
5326 }
5327 } else {
5328 newFormat += ch;
5329 }
5330 }
5331 nodep->text(newFormat);
5332 UINFO(9, " Display out " << nodep->text() << endl);
5333 }
5334 void visit(AstCReturn* nodep) override {
5335 assertAtStatement(nodep);
5336 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5337 }
5338 void visit(AstConstraintRef* nodep) override { userIterateChildren(nodep, nullptr); }
5339 void visit(AstDisplay* nodep) override {
5340 assertAtStatement(nodep);
5341 if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5342 // Just let all arguments seek their natural sizes
5343 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5344 }
5345 void visit(AstElabDisplay* nodep) override {
5346 assertAtStatement(nodep);
5347 // Just let all arguments seek their natural sizes
5348 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5349 if (!m_paramsOnly) {
5350 V3Const::constifyParamsEdit(nodep->fmtp()); // fmtp may change
5351 string text = nodep->fmtp()->text();
5352 if (text.empty()) text = "Elaboration system task message (IEEE 1800-2023 20.11)";
5353 switch (nodep->displayType()) {
5354 case VDisplayType::DT_INFO: nodep->v3warn(USERINFO, text); break;
5355 case VDisplayType::DT_ERROR: nodep->v3warn(USERERROR, text); break;
5356 case VDisplayType::DT_WARNING: nodep->v3warn(USERWARN, text); break;
5357 case VDisplayType::DT_FATAL: nodep->v3warn(USERFATAL, text); break;
5358 default: UASSERT_OBJ(false, nodep, "Unexpected elaboration display type");
5359 }
5360 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
5361 }
5362 }
5363 void visit(AstDumpCtl* nodep) override {
5364 assertAtStatement(nodep);
5365 // Just let all arguments seek their natural sizes
5366 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5367 }
5368 void visit(AstFOpen* nodep) override {
5369 // Although a system function in IEEE, here a statement which sets the file pointer (MCD)
5370 userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p());
5371 userIterateAndNext(nodep->modep(), WidthVP{SELF, BOTH}.p());
5372 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5373 }
5374 void visit(AstFOpenMcd* nodep) override {
5375 userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p());
5376 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5377 }
5378 void visit(AstFClose* nodep) override {
5379 assertAtStatement(nodep);
5380 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5381 }
5382 void visit(AstFError* nodep) override {
5383 if (m_vup->prelim()) {
5384 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5385 // Could be string or packed array
5386 userIterateAndNext(nodep->strp(), WidthVP{SELF, BOTH}.p());
5387 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5388 }
5389 }
5390 void visit(AstFEof* nodep) override {
5391 if (m_vup->prelim()) {
5392 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5393 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5394 }
5395 }
5396 void visit(AstFFlush* nodep) override {
5397 assertAtStatement(nodep);
5398 if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5399 }
5400 void visit(AstFRewind* nodep) override {
5401 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5402 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5403 }
5404 void visit(AstFTell* nodep) override {
5405 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5406 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5407 }
5408 void visit(AstFSeek* nodep) override {
5409 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5410 iterateCheckSigned32(nodep, "$fseek offset", nodep->offset(), BOTH);
5411 iterateCheckSigned32(nodep, "$fseek operation", nodep->operation(), BOTH);
5412 nodep->dtypeSetLogicUnsized(32, 1, VSigning::SIGNED); // Spec says integer return
5413 }
5414 void visit(AstFGetC* nodep) override {
5415 if (m_vup->prelim()) {
5416 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5417 nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return
5418 }
5419 }
5420 void visit(AstFGetS* nodep) override {
5421 if (m_vup->prelim()) {
5422 nodep->dtypeSetSigned32(); // Spec says integer return
5423 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5424 userIterateAndNext(nodep->strgp(), WidthVP{SELF, BOTH}.p());
5425 }
5426 }
5427 void visit(AstFUngetC* nodep) override {
5428 if (m_vup->prelim()) {
5429 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5430 iterateCheckSigned32(nodep, "$fungetc character", nodep->charp(), BOTH);
5431 nodep->dtypeSetLogicUnsized(32, 8, VSigning::SIGNED); // Spec says integer return
5432 }
5433 }
5434 void visit(AstFRead* nodep) override {
5435 if (m_vup->prelim()) {
5436 nodep->dtypeSetSigned32(); // Spec says integer return
5437 userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p());
5438 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5439 if (nodep->startp()) {
5440 iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH);
5441 }
5442 if (nodep->countp()) {
5443 iterateCheckSigned32(nodep, "$fread count", nodep->countp(), BOTH);
5444 }
5445 }
5446 }
5447 void visit(AstFScanF* nodep) override {
5448 if (m_vup->prelim()) {
5449 nodep->dtypeSetSigned32(); // Spec says integer return
5450 iterateCheckFileDesc(nodep, nodep->filep(), BOTH);
5451 userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
5452 }
5453 }
5454 void visit(AstSScanF* nodep) override {
5455 if (m_vup->prelim()) {
5456 nodep->dtypeSetSigned32(); // Spec says integer return
5457 userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
5458 userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
5459 }
5460 }
5461 void visit(AstStackTraceF* nodep) override { nodep->dtypeSetString(); }
5462 void visit(AstSysIgnore* nodep) override {
5463 userIterateAndNext(nodep->exprsp(), WidthVP{SELF, BOTH}.p());
5464 }
5465 void visit(AstSystemF* nodep) override {
5466 if (m_vup->prelim()) {
5467 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5468 nodep->dtypeSetSigned32(); // Spec says integer return
5469 }
5470 }
5471 void visit(AstSysFuncAsTask* nodep) override {
5472 assertAtStatement(nodep);
5473 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5474 }
5475 void visit(AstSystemT* nodep) override {
5476 assertAtStatement(nodep);
5477 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
5478 }
5479 void visit(AstNodeReadWriteMem* nodep) override {
5480 assertAtStatement(nodep);
5481 userIterateAndNext(nodep->filenamep(), WidthVP{SELF, BOTH}.p());
5482 userIterateAndNext(nodep->memp(), WidthVP{SELF, BOTH}.p());
5483 const AstNodeDType* subp = nullptr;
5484 if (const AstAssocArrayDType* adtypep
5485 = VN_CAST(nodep->memp()->dtypep()->skipRefp(), AssocArrayDType)) {
5486 subp = adtypep->subDTypep();
5487 if (!adtypep->keyDTypep()->skipRefp()->basicp()
5488 || !adtypep->keyDTypep()->skipRefp()->basicp()->keyword().isIntNumeric()) {
5489 nodep->memp()->v3error(nodep->verilogKwd()
5490 << " address/key must be integral (IEEE 1800-2023 21.4.1)");
5491 }
5492 } else if (const AstUnpackArrayDType* const adtypep
5493 = VN_CAST(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) {
5494 subp = adtypep->subDTypep();
5495 } else {
5496 nodep->memp()->v3warn(E_UNSUPPORTED,
5497 "Unsupported: "
5498 << nodep->verilogKwd()
5499 << " into other than unpacked or associative array");
5500 }
5501 if (subp
5502 && (!subp->skipRefp()->basicp()
5503 || !subp->skipRefp()->basicp()->keyword().isIntNumeric())) {
5504 nodep->memp()->v3warn(E_UNSUPPORTED,
5505 "Unsupported: " << nodep->verilogKwd()
5506 << " array values must be integral");
5507 }
5508 userIterateAndNext(nodep->lsbp(), WidthVP{SELF, BOTH}.p());
5509 userIterateAndNext(nodep->msbp(), WidthVP{SELF, BOTH}.p());
5510 }
5511 void visit(AstTestPlusArgs* nodep) override {
5512 if (m_vup->prelim()) {
5513 userIterateAndNext(nodep->searchp(), WidthVP{SELF, BOTH}.p());
5514 nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
5515 }
5516 }
5517 void visit(AstValuePlusArgs* nodep) override {
5518 if (m_vup->prelim()) {
5519 userIterateAndNext(nodep->searchp(), WidthVP{SELF, BOTH}.p());
5520 userIterateAndNext(nodep->outp(), WidthVP{SELF, BOTH}.p());
5521 nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
5522 }
5523 }
5524 void visit(AstTimeFormat* nodep) override {
5525 assertAtStatement(nodep);
5526 iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH);
5527 iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH);
5528 iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH);
5529 iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH);
5530 }
5531 void visit(AstUCStmt* nodep) override {
5532 // Just let all arguments seek their natural sizes
5533 assertAtStatement(nodep);
5534 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5535 }
5536 void visit(AstAssert* nodep) override {
5537 assertAtStatement(nodep);
5538 iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
5539 userIterateAndNext(nodep->passsp(), nullptr);
5540 userIterateAndNext(nodep->failsp(), nullptr);
5541 }
5542 void visit(AstAssertCtl* nodep) override {
5543 assertAtStatement(nodep);
5544 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5545 }
5546 void visit(AstAssertIntrinsic* nodep) override {
5547 assertAtStatement(nodep);
5548 iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
5549 userIterateAndNext(nodep->passsp(), nullptr);
5550 userIterateAndNext(nodep->failsp(), nullptr);
5551 }
5552 void visit(AstCover* nodep) override {
5553 assertAtStatement(nodep);
5554 iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
5555 userIterateAndNext(nodep->passsp(), nullptr);
5556 }
5557 void visit(AstRestrict* nodep) override {
5558 assertAtStatement(nodep);
5559 iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
5560 }
5561 void visit(AstPin* nodep) override {
5562 // if (debug()) nodep->dumpTree("- PinPre: ");
5563 // TOP LEVEL NODE
5564 if (nodep->modVarp() && nodep->modVarp()->isGParam()) {
5565 // Widthing handled as special init() case
5566 bool didWidth = false;
5567 if (auto* const patternp = VN_CAST(nodep->exprp(), Pattern)) {
5568 if (const AstVar* const modVarp = nodep->modVarp()) {
5569 // Convert BracketArrayDType
5570 userIterate(modVarp->childDTypep(),
5571 WidthVP{SELF, BOTH}.p()); // May relink pointed to node
5572 AstNodeDType* const setDtp = modVarp->childDTypep()->cloneTree(false);
5573 if (!patternp->childDTypep()) patternp->childDTypep(setDtp);
5574 userIterateChildren(nodep, WidthVP{setDtp, BOTH}.p());
5575 didWidth = true;
5576 }
5577 }
5578 if (!didWidth) userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
5579 } else if (!m_paramsOnly) {
5580 if (!nodep->modVarp()->didWidth()) {
5581 // Var hasn't been widthed, so make it so.
5582 userIterate(nodep->modVarp(), nullptr);
5583 }
5584 if (!nodep->exprp()) { // No-connect
5585 return;
5586 }
5587 // Very much like like an assignment, but which side is LH/RHS
5588 // depends on pin being a in/output/inout.
5589 userIterateAndNext(nodep->exprp(), WidthVP{nodep->modVarp()->dtypep(), PRELIM}.p());
5590 AstNodeDType* modDTypep = nodep->modVarp()->dtypep();
5591 AstNodeDType* conDTypep = nodep->exprp()->dtypep();
5592 UASSERT_OBJ(modDTypep, nodep, "Unlinked pin data type");
5593 UASSERT_OBJ(conDTypep, nodep, "Unlinked pin data type");
5594 modDTypep = modDTypep->skipRefp();
5595 conDTypep = conDTypep->skipRefp();
5596 AstNodeDType* subDTypep = modDTypep;
5597 const int modwidth = modDTypep->width();
5598 const int conwidth = conDTypep->width();
5599 if (conDTypep == modDTypep // If match, we're golden
5600 || similarDTypeRecurse(conDTypep, modDTypep)) {
5601 userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
5602 } else if (m_cellp->rangep()) {
5603 const int numInsts = m_cellp->rangep()->elementsConst();
5604 if (conwidth == modwidth) {
5605 // Arrayed instants: widths match so connect to each instance
5606 subDTypep = conDTypep; // = same expr dtype
5607 } else if (conwidth == numInsts * modwidth) {
5608 // Arrayed instants: one bit for each of the instants (each
5609 // assign is 1 modwidth wide)
5610 subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype)
5611 } else {
5612 // Must be a error according to spec
5613 // (Because we need to know if to connect to one or all instants)
5614 nodep->v3error(ucfirst(nodep->prettyOperatorName())
5615 << " as part of a module instance array"
5616 << " requires " << modwidth << " or " << modwidth * numInsts
5617 << " bits, but connection's "
5618 << nodep->exprp()->prettyTypeName() << " generates " << conwidth
5619 << " bits. (IEEE 1800-2023 23.3.3)");
5620 subDTypep = conDTypep; // = same expr dtype
5621 }
5622 userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
5623 } else {
5624 if (nodep->modVarp()->direction() == VDirection::REF) {
5625 nodep->v3error("Ref connection "
5626 << nodep->modVarp()->prettyNameQ()
5627 << " requires matching types;"
5628 << " ref requires " << modDTypep->prettyDTypeNameQ()
5629 << " data type but connection is "
5630 << conDTypep->prettyDTypeNameQ() << " data type.");
5631 } else if (nodep->modVarp()->isTristate()) {
5632 if (modwidth != conwidth) {
5633 // Ideally should call pinReconnectSimple which would tolerate this
5634 // then have a conversion warning
5635 nodep->v3warn(E_UNSUPPORTED,
5636 "Unsupported: " << ucfirst(nodep->prettyOperatorName())
5637 << " to inout signal requires " << modwidth
5638 << " bits, but connection's "
5639 << nodep->exprp()->prettyTypeName()
5640 << " generates " << conwidth << " bits.");
5641 // otherwise would need some mess to force both sides to proper size
5642 }
5643 } else if (nodep->modVarp()->direction().isWritable()
5644 && ((conDTypep->isDouble() && !modDTypep->isDouble())
5645 || (!conDTypep->isDouble() && modDTypep->isDouble()))) {
5646 nodep->v3warn(E_UNSUPPORTED,
5647 "Unsupported: " << ucfirst(nodep->prettyOperatorName())
5648 << " connects real to non-real");
5649 }
5650
5651 // Check if an interface is connected to a non-interface and vice versa
5652 if ((VN_IS(modDTypep, IfaceRefDType) && !VN_IS(conDTypep, IfaceRefDType))
5653 || (VN_IS(conDTypep, IfaceRefDType) && !VN_IS(modDTypep, IfaceRefDType))) {
5654 nodep->v3error("Illegal " << nodep->prettyOperatorName() << ","
5655 << " mismatch between port which is"
5656 << (VN_CAST(modDTypep, IfaceRefDType) ? "" : " not")
5657 << " an interface,"
5658 << " and expression which is"
5659 << (VN_CAST(conDTypep, IfaceRefDType) ? "" : " not")
5660 << " an interface.");
5661 }
5662
5663 // TODO Simple dtype checking, should be a more general check
5664 const AstNodeArrayDType* const exprArrayp = VN_CAST(conDTypep, UnpackArrayDType);
5665 const AstNodeArrayDType* const modArrayp = VN_CAST(modDTypep, UnpackArrayDType);
5666 if (exprArrayp && modArrayp && VN_IS(exprArrayp->subDTypep(), IfaceRefDType)
5667 && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) {
5668 const int exprSize = exprArrayp->declRange().elements();
5669 const int modSize = modArrayp->declRange().elements();
5670 nodep->v3error("Illegal "
5671 << nodep->prettyOperatorName() << ","
5672 << " mismatch between port which is an interface array of size "
5673 << modSize << ","
5674 << " and expression which is an interface array of size "
5675 << exprSize << ".");
5676 UINFO(1, " Related lo: " << modDTypep << endl);
5677 UINFO(1, " Related hi: " << conDTypep << endl);
5678 } else if ((exprArrayp && !modArrayp) || (!exprArrayp && modArrayp)) {
5679 nodep->v3error("Illegal " << nodep->prettyOperatorName() << ","
5680 << " mismatch between port which is"
5681 << (modArrayp ? "" : " not") << " an array,"
5682 << " and expression which is"
5683 << (exprArrayp ? "" : " not")
5684 << " an array. (IEEE 1800-2023 7.6)");
5685 UINFO(1, " Related lo: " << modDTypep << endl);
5686 UINFO(1, " Related hi: " << conDTypep << endl);
5687 }
5688 iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep);
5689 }
5690 }
5691 // if (debug()) nodep->dumpTree("- PinOut: ");
5692 }
5693 void visit(AstCell* nodep) override {
5694 VL_RESTORER(m_cellp);
5695 m_cellp = nodep;
5696 if (!m_paramsOnly) {
5697 if (VN_IS(nodep->modp(), NotFoundModule)) {
5698 // We've resolved parameters and hit a module that we couldn't resolve. It's
5699 // finally time to report it.
5700 // Note only here in V3Width as this is first visitor after V3Dead.
5701 nodep->modNameFileline()->v3error("Cannot find file containing module: '"
5702 << nodep->modName() << "'");
5703 v3Global.opt.filePathLookedMsg(nodep->modNameFileline(), nodep->modName());
5704 }
5705 if (nodep->rangep()) userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
5706 userIterateAndNext(nodep->pinsp(), nullptr);
5707 }
5708 userIterateAndNext(nodep->paramsp(), nullptr);
5709 }
5710 void visit(AstGatePin* nodep) override {
5711 if (m_vup->prelim()) {
5712 userIterateAndNext(nodep->rangep(), WidthVP{SELF, BOTH}.p());
5713 userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
5714 nodep->dtypeFrom(nodep->rangep());
5715 // Very much like like an pin
5716 const AstNodeDType* const conDTypep = nodep->exprp()->dtypep();
5717 const int numInsts = nodep->rangep()->elementsConst();
5718 const int modwidth = numInsts;
5719 const int conwidth = conDTypep->width();
5720 if (conwidth == 1 && modwidth > 1) { // Multiple connections
5721 AstNodeDType* const subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric());
5722 userIterateAndNext(nodep->exprp(), WidthVP{subDTypep, FINAL}.p());
5723 AstNode* const newp
5724 = new AstReplicate{nodep->fileline(), nodep->exprp()->unlinkFrBack(),
5725 static_cast<uint32_t>(numInsts)};
5726 nodep->replaceWith(newp);
5727 } else {
5728 // Eliminating so pass down all of vup
5729 userIterateAndNext(nodep->exprp(), m_vup);
5730 nodep->replaceWith(nodep->exprp()->unlinkFrBack());
5731 }
5732 VL_DO_DANGLING(pushDeletep(nodep), nodep);
5733 }
5734 }
5735 void visit(AstNodeFTask* nodep) override {
5736 // Grab width from the output variable (if it's a function)
5737 if (nodep->didWidth()) return;
5738 if (nodep->doingWidth()) {
5739 UINFO(5, "Recursive function or task call: " << nodep);
5740 nodep->recursive(true);
5741 nodep->didWidth(true);
5742 return;
5743 }
5744 if (nodep->classMethod() && nodep->name() == "rand_mode") {
5745 nodep->v3error("The 'rand_mode' method is built-in and cannot be overridden"
5746 " (IEEE 1800-2023 18.8)");
5747 } else if (nodep->classMethod() && nodep->name() == "constraint_mode") {
5748 nodep->v3error("The 'constraint_mode' method is built-in and cannot be overridden"
5749 " (IEEE 1800-2023 18.9)");
5750 }
5751 // Function hasn't been widthed, so make it so.
5752 // Would use user1 etc, but V3Width called from too many places to spend a user
5753 nodep->doingWidth(true);
5754 VL_RESTORER(m_funcp);
5755 VL_RESTORER(m_ftaskp);
5756 m_ftaskp = nodep;
5757 // First width the function variable, as if is a recursive function we need data type
5758 if (nodep->fvarp()) userIterate(nodep->fvarp(), nullptr);
5759 if (nodep->isConstructor()) {
5760 // Pretend it's void so less special casing needed when look at dtypes
5761 nodep->dtypeSetVoid();
5762 } else if (nodep->fvarp()) {
5763 m_funcp = VN_AS(nodep, Func);
5764 UASSERT_OBJ(m_funcp, nodep, "FTask with function variable, but isn't a function");
5765 nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep()
5766 } else if (VN_IS(nodep, Property)) {
5767 nodep->dtypeSetBit();
5768 }
5769 WidthVP* vup = nullptr;
5770 if (VN_IS(nodep, Property)) vup = WidthVP{SELF, BOTH}.p();
5771 userIterateChildren(nodep, vup);
5772
5773 nodep->didWidth(true);
5774 nodep->doingWidth(false);
5775 if (nodep->dpiImport() && !nodep->dpiOpenParent() && markHasOpenArray(nodep)) {
5776 nodep->dpiOpenParentInc(); // Mark so V3Task will wait for a child to build calling
5777 // func
5778 }
5779 }
5780 void visit(AstConstraint* nodep) override {
5781 if (nodep->didWidth()) return;
5782 VL_RESTORER(m_constraintp);
5783 m_constraintp = nodep;
5784 userIterateChildren(nodep, nullptr);
5785 nodep->didWidth(true);
5786 }
5787 void visit(AstProperty* nodep) override {
5788 if (nodep->didWidth()) return;
5789 if (nodep->doingWidth()) {
5790 UINFO(5, "Recursive property call: " << nodep);
5791 nodep->v3warn(E_UNSUPPORTED,
5792 "Unsupported: Recursive property call: " << nodep->prettyNameQ());
5793 nodep->recursive(true);
5794 nodep->didWidth(true);
5795 return;
5796 }
5797 nodep->doingWidth(true);
5798 m_ftaskp = nodep;
5799 // Property call will be replaced by property body in V3AssertPre. Property body has bit
5800 // dtype, so set it here too
5801 nodep->dtypeSetBit();
5802 for (AstNode* propStmtp = nodep->stmtsp(); propStmtp; propStmtp = propStmtp->nextp()) {
5803 if (VN_IS(propStmtp, Var)) {
5804 userIterate(propStmtp, nullptr);
5805 } else if (VN_IS(propStmtp, PropSpec)) {
5806 iterateCheckSizedSelf(nodep, "PropSpec", propStmtp, SELF, BOTH);
5807 } else {
5808 propStmtp->v3fatal("Invalid statement under AstProperty");
5809 }
5810 }
5811 nodep->didWidth(true);
5812 nodep->doingWidth(false);
5813 m_ftaskp = nullptr;
5814 }
5815 void visit(AstReturn* nodep) override {
5816 // IEEE: Assignment-like context
5817 assertAtStatement(nodep);
5818 if (!m_funcp) {
5819 if (nodep->lhsp()) { // Return w/o value ok other places
5820 nodep->v3error("Return with return value isn't underneath a function");
5821 }
5822 } else {
5823 if (nodep->lhsp()) {
5824 // Function hasn't been widthed, so make it so.
5825 nodep->dtypeFrom(m_funcp->fvarp());
5826 // AstPattern requires assignments to pass datatype on PRELIM
5827 userIterateAndNext(nodep->lhsp(), WidthVP{nodep->dtypep(), PRELIM}.p());
5828 iterateCheckAssign(nodep, "Return value", nodep->lhsp(), FINAL, nodep->dtypep());
5829 }
5830 }
5831 }
5832
5833 AstPackage* getItemPackage(AstNode* pkgItemp) {
5834 while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) {
5835 pkgItemp = pkgItemp->backp();
5836 }
5837 return VN_CAST(pkgItemp->backp(), Package);
5838 }
5839 void visit(AstFuncRef* nodep) override {
5840 visit(static_cast<AstNodeFTaskRef*>(nodep));
5841 if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) {
5842 UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expection");
5843 if (m_vup->prelim())
5844 nodep->v3error(
5845 "Cannot call a task/void-function as a function: " << nodep->prettyNameQ());
5846 nodep->dtypeSetVoid();
5847 } else {
5848 nodep->dtypeFrom(nodep->taskp());
5849 }
5850 if (nodep->fileline()->timingOn()) {
5851 AstNodeModule* const classp = nodep->classOrPackagep();
5852 if (nodep->name() == "self" && classp->name() == "process") {
5853 // Find if package the class is in is std::
5854 AstPackage* const packagep = getItemPackage(classp);
5855 if (packagep && packagep->name() == "std") {
5856 methodCallWarnTiming(nodep, "process");
5857 }
5858 }
5859 }
5860 // if (debug()) nodep->dumpTree("- FuncOut: ");
5861 }
5862 // Returns true if dtypep0 and dtypep1 have same dimensions
5863 static bool areSameSize(AstUnpackArrayDType* dtypep0, AstUnpackArrayDType* dtypep1) {
5864 const std::vector<AstUnpackArrayDType*> dims0 = dtypep0->unpackDimensions();
5865 const std::vector<AstUnpackArrayDType*> dims1 = dtypep1->unpackDimensions();
5866 if (dims0.size() != dims1.size()) return false;
5867 for (size_t i = 0; i < dims0.size(); ++i) {
5868 if (dims0[i]->elementsConst() != dims1[i]->elementsConst()) return false;
5869 }
5870 return true;
5871 }
5872 // Makes sure that port and pin have same size and same datatype
5873 void checkUnpackedArrayArgs(AstVar* portp, AstNode* pinp) {
5874 if (AstUnpackArrayDType* const portDtypep
5875 = VN_CAST(portp->dtypep()->skipRefp(), UnpackArrayDType)) {
5876 if (AstUnpackArrayDType* const pinDtypep
5877 = VN_CAST(pinp->dtypep()->skipRefp(), UnpackArrayDType)) {
5878 if (!areSameSize(portDtypep, pinDtypep)) {
5879 pinp->v3warn(E_UNSUPPORTED,
5880 "Shape of the argument does not match the shape of the parameter "
5881 << "(" << pinDtypep->prettyDTypeNameQ() << " v.s. "
5882 << portDtypep->prettyDTypeNameQ() << ")");
5883 }
5884 if (portDtypep->basicp()->width() != pinDtypep->basicp()->width()
5885 || (portDtypep->basicp()->keyword() != pinDtypep->basicp()->keyword()
5886 && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT
5887 && pinDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC)
5888 && !(portDtypep->basicp()->keyword() == VBasicDTypeKwd::LOGIC
5889 && pinDtypep->basicp()->keyword()
5890 == VBasicDTypeKwd::LOGIC_IMPLICIT))) {
5891 pinp->v3warn(E_UNSUPPORTED,
5892 "Shape of the argument does not match the shape of the parameter "
5893 << "(" << pinDtypep->basicp()->prettyDTypeNameQ() << " v.s. "
5894 << portDtypep->basicp()->prettyDTypeNameQ() << ")");
5895 }
5896 } else {
5897 pinp->v3warn(E_UNSUPPORTED, "Argument is not an unpacked array while parameter "
5898 << portp->prettyNameQ() << " is");
5899 }
5900 }
5901 }
5902 void processFTaskRefArgs(AstNodeFTaskRef* nodep) {
5903 // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
5904 // Function hasn't been widthed, so make it so.
5905 UINFO(5, " FTASKREF " << nodep << endl);
5906 UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
5907 if (nodep->didWidth()) return;
5908 userIterate(nodep->taskp(), nullptr);
5909 //
5910 // And do the arguments to the task/function too
5911 do {
5912 reloop:
5913 // taskConnects may create a new task, and change nodep->taskp()
5914 const V3TaskConnects tconnects
5915 = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp(), &m_taskConnectState);
5916 if (m_taskConnectState.didWrap()) m_memberMap.clear(); // As added a member
5917 for (const auto& tconnect : tconnects) {
5918 const AstVar* const portp = tconnect.first;
5919 AstArg* const argp = tconnect.second;
5920 AstNodeExpr* pinp = argp->exprp();
5921 if (!pinp) continue; // Argument error we'll find later
5922 // Prelim may cause the node to get replaced; we've lost our
5923 // pointer, so need to iterate separately later
5924 if (portp->attrSFormat()
5925 && (!VN_IS(pinp, SFormatF) || pinp->nextp())) { // Not already done
5926 UINFO(4, " sformat via metacomment: " << nodep << endl);
5927 VNRelinker handle;
5928 argp->unlinkFrBackWithNext(&handle); // Format + additional args, if any
5929 AstNodeExpr* argsp = nullptr;
5930 while (AstArg* const nextargp = VN_AS(argp->nextp(), Arg)) {
5931 // Expression goes to SFormatF
5932 argsp = argsp->addNext(nextargp->exprp()->unlinkFrBackWithNext());
5933 nextargp->unlinkFrBack()->deleteTree(); // Remove the call's Arg wrapper
5934 }
5935 string format;
5936 if (VN_IS(pinp, Const)) {
5937 format = VN_AS(pinp, Const)->num().toString();
5938 } else {
5939 pinp->v3error(
5940 "Format to $display-like function must have constant format string");
5941 }
5942 VL_DO_DANGLING(pushDeletep(argp), argp);
5943 AstSFormatF* const newp
5944 = new AstSFormatF{nodep->fileline(), format, false, argsp};
5945 if (!newp->scopeNamep() && newp->formatScopeTracking()) {
5946 newp->scopeNamep(new AstScopeName{newp->fileline(), true});
5947 }
5948 handle.relink(new AstArg{newp->fileline(), "", newp});
5949 // Connection list is now incorrect (has extra args in it).
5950 goto reloop; // so exit early; next loop will correct it
5951 } //
5952 else if (portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING
5953 && !VN_IS(pinp, CvtPackString)
5954 && !VN_IS(pinp, SFormatF) // Already generates a string
5955 && !VN_IS(portp->dtypep(), UnpackArrayDType) // Unpacked array must match
5956 && !(VN_IS(pinp, VarRef)
5957 && VN_AS(pinp, VarRef)->varp()->basicp()->keyword()
5958 == VBasicDTypeKwd::STRING)) {
5959 UINFO(4, " Add CvtPackString: " << pinp << endl);
5960 VNRelinker handle;
5961 pinp->unlinkFrBack(&handle); // No next, that's the next pin
5962 AstNodeExpr* const newp = new AstCvtPackString{pinp->fileline(), pinp};
5963 handle.relink(newp);
5964 pinp = newp;
5965 }
5966 // AstPattern requires assignments to pass datatype on PRELIM
5967 VL_DO_DANGLING(userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p()), pinp);
5968 }
5969 } while (false);
5970 // Stage 2
5971 {
5972 const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
5973 for (const auto& tconnect : tconnects) {
5974 AstVar* const portp = tconnect.first;
5975 const AstArg* const argp = tconnect.second;
5976 AstNodeExpr* const pinp = argp->exprp();
5977 if (!pinp) continue; // Argument error we'll find later
5978 // Change data types based on above accept completion
5979 if (nodep->taskp()->dpiImport()) checkUnpackedArrayArgs(portp, pinp);
5980 if (portp->isDouble()) VL_DO_DANGLING(spliceCvtD(pinp), pinp);
5981 }
5982 }
5983 // Stage 3
5984 {
5985 const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
5986 for (const auto& tconnect : tconnects) {
5987 const AstVar* const portp = tconnect.first;
5988 const AstArg* const argp = tconnect.second;
5989 AstNodeExpr* const pinp = argp->exprp();
5990 if (!pinp) continue; // Argument error we'll find later
5991 // Do PRELIM again, because above accept may have exited early
5992 // due to node replacement
5993 userIterate(pinp, WidthVP{portp->dtypep(), PRELIM}.p());
5994 }
5995 }
5996 // Cleanup any open arrays
5997 if (markHasOpenArray(nodep->taskp())) makeOpenArrayShell(nodep);
5998 // Stage 4
5999 {
6000 const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
6001 for (const auto& tconnect : tconnects) {
6002 const AstVar* const portp = tconnect.first;
6003 const AstArg* const argp = tconnect.second;
6004 AstNodeExpr* const pinp = argp->exprp();
6005 if (!pinp) continue; // Argument error we'll find later
6006 AstNodeDType* const portDTypep = portp->dtypep()->skipRefToEnump();
6007 const AstNodeDType* const pinDTypep = pinp->dtypep()->skipRefToEnump();
6008 if (portp->direction() == VDirection::REF
6009 && !similarDTypeRecurse(portDTypep, pinDTypep)) {
6010 pinp->v3error("Ref argument requires matching types;"
6011 << " port " << portp->prettyNameQ() << " requires "
6012 << portDTypep->prettyDTypeNameQ() << " but connection is "
6013 << pinDTypep->prettyDTypeNameQ() << ".");
6014 } else if (portp->isWritable() && pinp->width() != portp->width()) {
6015 pinp->v3widthWarn(portp->width(), pinp->width(),
6016 "Function output argument "
6017 << portp->prettyNameQ() << " requires " << portp->width()
6018 << " bits, but connection's " << pinp->prettyTypeName()
6019 << " generates " << pinp->width() << " bits.");
6020 VNRelinker relinkHandle;
6021 pinp->unlinkFrBack(&relinkHandle);
6022 AstNodeExpr* const newp = new AstResizeLValue{pinp->fileline(), pinp};
6023 relinkHandle.relink(newp);
6024 }
6025 if (portp->isWritable()) V3LinkLValue::linkLValueSet(pinp);
6026 if (!portp->basicp() || portp->basicp()->isOpaque()) {
6027 checkClassAssign(nodep, "Function Argument", pinp, portDTypep);
6028 userIterate(pinp, WidthVP{portDTypep, FINAL}.p());
6029 } else {
6030 iterateCheckAssign(nodep, "Function Argument", pinp, FINAL, portDTypep);
6031 }
6032 }
6033 }
6034 }
6035 void visit(AstNodeFTaskRef* nodep) override {
6036 // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
6037 // Function hasn't been widthed, so make it so.
6038 UINFO(5, " FTASKREF " << nodep << endl);
6039 AstWith* withp = nullptr;
6040 if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
6041 v3Global.useRandomizeMethods(true);
6042 nodep->dtypep(nodep->findBasicDType(VBasicDTypeKwd::INT));
6043 return; // Handled in V3Randomize
6044 } else if (nodep->name() == "randomize" || nodep->name() == "srandom"
6045 || (!nodep->taskp()
6046 && (nodep->name() == "get_randstate"
6047 || nodep->name() == "set_randstate"))) {
6048 // TODO perhaps this should move to V3LinkDot
6049 AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
6050 if (!classp) {
6051 AstNodeDType* const adtypep = nodep->findBitDType();
6052 withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
6053 adtypep->findBitDType(), adtypep);
6054 for (const AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp())
6055 userIterateAndNext(VN_AS(argp, Arg)->exprp(), WidthVP{SELF, BOTH}.p());
6056 nodep->addPinsp(withp);
6057 nodep->v3warn(CONSTRAINTIGN, "std::randomize ignored (unsupported)");
6058 nodep->replaceWith(new AstConst{nodep->fileline(), 0});
6059 VL_DO_DANGLING(pushDeletep(nodep), nodep);
6060 return;
6061 }
6062 UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot");
6063 if (nodep->name() == "randomize") {
6064 AstClassRefDType* const adtypep
6065 = new AstClassRefDType{nodep->fileline(), classp, nullptr};
6066 v3Global.rootp()->typeTablep()->addTypesp(adtypep);
6067 withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
6068 adtypep->findBitDType(), adtypep);
6069 handleRandomizeArgs(nodep, classp);
6070 } else if (nodep->name() == "srandom") {
6071 nodep->taskp(V3Randomize::newSRandomFunc(m_memberMap, classp));
6072 m_memberMap.clear();
6073 } else if (nodep->name() == "get_randstate") {
6074 methodOkArguments(nodep, 0, 0);
6075 classp->baseMostClassp()->needRNG(true);
6076 v3Global.useRandomizeMethods(true);
6077 AstCExpr* const newp
6078 = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true};
6079 newp->dtypeSetString();
6080 nodep->replaceWith(newp);
6081 VL_DO_DANGLING(pushDeletep(nodep), nodep);
6082 return;
6083 } else if (nodep->name() == "set_randstate") {
6084 methodOkArguments(nodep, 1, 1);
6085 AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit
6086 iterateCheckString(nodep, "LHS", expr1p, BOTH);
6087 AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp();
6088 classp->baseMostClassp()->needRNG(true);
6089 v3Global.useRandomizeMethods(true);
6090 AstCExpr* const newp
6091 = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true};
6092 newp->addExprsp(exprp->unlinkFrBack());
6093 newp->addExprsp(new AstText{nodep->fileline(), ")", true});
6094 newp->dtypeSetString();
6095 nodep->replaceWith(newp);
6096 VL_DO_DANGLING(pushDeletep(nodep), nodep);
6097 return;
6098 } else {
6099 UASSERT_OBJ(false, nodep, "Bad case");
6100 }
6101 }
6102 UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
6103 if (nodep->didWidth()) return;
6104 if ((nodep->taskp()->classMethod() && !nodep->taskp()->isStatic())
6105 && !VN_IS(m_procedurep, InitialAutomatic)
6106 && (!m_ftaskp || !m_ftaskp->classMethod() || m_ftaskp->isStatic()) && !m_constraintp) {
6107 nodep->v3error("Cannot call non-static member function "
6108 << nodep->prettyNameQ() << " without object (IEEE 1800-2023 8.10)");
6109 }
6110 // And do the arguments to the task/function too
6111 processFTaskRefArgs(nodep);
6112 nodep->addPinsp(withp);
6113 nodep->didWidth(true);
6114 // See steps that follow in visit(AstFuncRef*)
6115 }
6116 175 void visit(AstNodeProcedure* nodep) override {
6117 175 assertAtStatement(nodep);
6118
1/2
✓ Branch 0 taken 175 times.
✗ Branch 1 not taken.
175 VL_RESTORER(m_procedurep);
6119 175 m_procedurep = nodep;
6120 userIterateChildren(nodep, nullptr);
6121 175 }
6122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 void visit(AstSenItem* nodep) override {
6123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 160 times.
160 UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge");
6124 // Optimize concat/replicate senitems; this has to be done here at the latest, otherwise we
6125 // emit WIDTHCONCAT if there are unsized constants
6126 if (VN_IS(nodep->sensp(), Concat) || VN_IS(nodep->sensp(), Replicate)) {
6127 auto* const concatOrReplp = VN_CAST(nodep->sensp(), NodeBiop);
6128 auto* const rhsp = concatOrReplp->rhsp()->unlinkFrBack();
6129 if (nodep->edgeType() == VEdgeType::ET_CHANGED) {
6130 // If it's ET_CHANGED, split concatenations into multiple senitems
6131 auto* const lhsp = concatOrReplp->lhsp()->unlinkFrBack();
6132 nodep->addNextHere(new AstSenItem{lhsp->fileline(), nodep->edgeType(), lhsp});
6133 } // Else only use the RHS
6134 nodep->replaceWith(new AstSenItem{rhsp->fileline(), nodep->edgeType(), rhsp});
6135 VL_DO_DANGLING(nodep->deleteTree(), nodep);
6136 } else {
6137
1/2
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
320 userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
6138
1/2
✓ Branch 0 taken 160 times.
✗ Branch 1 not taken.
160 if (nodep->edgeType().anEdge()) {
6139 AstNodeDType* const sensDtp = nodep->sensp()->dtypep()->skipRefp();
6140
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
160 if (sensDtp->isDouble()) {
6141 nodep->sensp()->v3error(
6142 "Edge event control not legal on real type (IEEE 1800-2023 6.12.1)");
6143
1/2
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
160 } else if (sensDtp->basicp() && !sensDtp->basicp()->keyword().isIntNumeric()) {
6144 nodep->sensp()->v3error("Edge event control not legal on non-integral type "
6145 "(IEEE 1800-2023 9.4.2)");
6146 }
6147 }
6148 }
6149 160 }
6150 void visit(AstClockingItem* nodep) override {
6151 nodep->exprp()->foreach([nodep](AstVarRef* const refp) {
6152 refp->access(nodep->direction().isWritable() ? VAccess::WRITE : VAccess::READ);
6153 });
6154 userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p());
6155 }
6156 void visit(AstWait* nodep) override {
6157 if (VN_IS(m_ftaskp, Func)) {
6158 nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
6159 "(IEEE 1800-2023 13.4.4)");
6160 VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
6161 return;
6162 }
6163 if (nodep->fileline()->timingOn()) {
6164 if (v3Global.opt.timing().isSetTrue()) {
6165 iterateCheckBool(nodep, "Wait", nodep->condp(),
6166 BOTH); // it's like an if() condition.
6167 // TODO check also inside complex event expressions
6168 if (AstNodeVarRef* const varrefp = VN_CAST(nodep->condp(), NodeVarRef)) {
6169 if (varrefp->isEvent()) {
6170 varrefp->v3error("Wait statement conditions do not take raw events"
6171 " (IEEE 1800-2023 15.5.3)\n"
6172 << varrefp->warnMore() << "... Suggest use '"
6173 << varrefp->prettyName() << ".triggered'");
6174 }
6175 }
6176 iterateNull(nodep->stmtsp());
6177 return;
6178 } else if (v3Global.opt.timing().isSetFalse()) {
6179 nodep->v3warn(E_NOTIMING, "Wait statements require --timing");
6180 } else {
6181 nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how wait "
6182 "statements should be handled");
6183 }
6184 }
6185 // If we ignore timing:
6186 // Statements we'll just execute immediately; equivalent to if they followed this
6187 if (AstNode* const stmtsp = nodep->stmtsp()) {
6188 stmtsp->unlinkFrBackWithNext();
6189 nodep->replaceWith(stmtsp);
6190 } else {
6191 nodep->unlinkFrBack();
6192 }
6193 VL_DO_DANGLING(nodep->deleteTree(), nodep);
6194 }
6195 void visit(AstWith* nodep) override {
6196 // Should otherwise be underneath a method call
6197 AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
6198 {
6199 VL_RESTORER(m_withp);
6200 m_withp = nodep;
6201 userIterateChildren(nodep->indexArgRefp(), nullptr);
6202 userIterateChildren(nodep->valueArgRefp(), nullptr);
6203 if (!nodep->exprp()->hasDType()) {
6204 userIterateAndNext(nodep->exprp(), nullptr);
6205 } else {
6206 if (vdtypep) {
6207 userIterateAndNext(nodep->exprp(), WidthVP{nodep->dtypep(), PRELIM}.p());
6208 } else { // 'sort with' allows arbitrary type
6209 userIterateAndNext(nodep->exprp(), WidthVP{SELF, PRELIM}.p());
6210 }
6211 }
6212
6213 if (!nodep->exprp()->hasDType()) {
6214 nodep->dtypeSetVoid();
6215 } else {
6216 nodep->dtypeFrom(nodep->exprp());
6217 iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL,
6218 nodep->dtypep());
6219 }
6220 }
6221 }
6222 void visit(AstLambdaArgRef* nodep) override {
6223 UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda");
6224 if (nodep->index()) {
6225 nodep->dtypeFrom(m_withp->indexArgRefp());
6226 } else {
6227 nodep->dtypeFrom(m_withp->valueArgRefp());
6228 }
6229 }
6230 80 void visit(AstNetlist* nodep) override {
6231 // Iterate modules backwards, in bottom-up order. That's faster
6232 userIterateChildrenBackwardsConst(nodep, nullptr);
6233 80 }
6234
6235 //--------------------
6236 // Default
6237 void visit(AstNodeExpr* nodep) override {
6238 if (!nodep->didWidth()) {
6239 nodep->v3fatalSrc(
6240 "Visit function missing? Widthed function missing for math node: " << nodep);
6241 }
6242 userIterateChildren(nodep, nullptr);
6243 }
6244 480 void visit(AstNode* nodep) override {
6245 // Default: Just iterate
6246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 480 times.
480 UASSERT_OBJ(!m_vup, nodep,
6247 "Visit function missing? Widthed expectation for this node: " << nodep);
6248 userIterateChildren(nodep, nullptr);
6249 480 }
6250
6251 //----------------------------------------------------------------------
6252 // WIDTH METHODs -- all iterate
6253
6254 void visit_Or_Lu64(AstNodeUniop* nodep) {
6255 // CALLER: AstBitsToRealD
6256 // Real: Output real
6257 // LHS presumed self-determined, then coerced to real
6258 if (m_vup->prelim()) { // First stage evaluation
6259 nodep->dtypeSetDouble();
6260 AstNodeDType* const subDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED);
6261 // Self-determined operand
6262 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
6263 iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
6264 }
6265 }
6266 void visit(AstIToRD* nodep) override {
6267 // Real: Output real
6268 // LHS presumed self-determined, then coerced to real
6269 if (m_vup->prelim()) { // First stage evaluation
6270 nodep->dtypeSetDouble();
6271 // Self-determined operand (TODO check if numeric type)
6272 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
6273 if (nodep->lhsp()->isSigned()) {
6274 nodep->replaceWith(
6275 new AstISToRD{nodep->fileline(), nodep->lhsp()->unlinkFrBack()});
6276 VL_DO_DANGLING(nodep->deleteTree(), nodep);
6277 }
6278 }
6279 }
6280 void visit(AstISToRD* nodep) override {
6281 // Real: Output real
6282 // LHS presumed self-determined, then coerced to real
6283 if (m_vup->prelim()) { // First stage evaluation
6284 nodep->dtypeSetDouble();
6285 // Self-determined operand (TODO check if numeric type)
6286 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
6287 }
6288 }
6289 void visit_Os32_Lr(AstNodeUniop* nodep) {
6290 // CALLER: RToI
6291 // Real: LHS real
6292 // LHS presumed self-determined, then coerced to real
6293 if (m_vup->prelim()) { // First stage evaluation
6294 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6295 nodep->dtypeSetSigned32();
6296 }
6297 }
6298 void visit_Ou64_Lr(AstNodeUniop* nodep) {
6299 // CALLER: RealToBits
6300 // Real: LHS real
6301 // LHS presumed self-determined, then coerced to real
6302 if (m_vup->prelim()) { // First stage evaluation
6303 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6304 nodep->dtypeSetUInt64();
6305 }
6306 }
6307
6308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 461 times.
461 void visit_log_not(AstNode* nodep) {
6309 // CALLER: LogNot
6310 // Width-check: lhs 1 bit
6311 // Real: Allowed; implicitly compares with zero
6312 // We calculate the width of the UNDER expression.
6313 // We then check its width to see if it's legal, and edit if not
6314 // We finally set the width of our output
6315 // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd):
6316 // LHS is self-determined
6317 // Width: 1 bit out
6318 // Sign: unsigned out (11.8.1)
6319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 461 times.
461 UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
6320
2/2
✓ Branch 0 taken 293 times.
✓ Branch 1 taken 168 times.
461 if (m_vup->prelim()) {
6321 293 iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH);
6322 293 nodep->dtypeSetBit();
6323 }
6324 461 }
6325 196 void visit_log_and_or(AstNodeBiop* nodep) {
6326 // CALLER: LogAnd, LogOr, LogEq, LogIf
6327 // Widths: 1 bit out, lhs 1 bit, rhs 1 bit
6328 // IEEE-2012 Table 11-21:
6329 // LHS is self-determined
6330 // RHS is self-determined
6331
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 98 times.
196 if (m_vup->prelim()) {
6332 98 iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH);
6333 98 iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH);
6334 98 nodep->dtypeSetBit();
6335 }
6336 196 }
6337 601 void visit_red_and_or(AstNodeUniop* nodep) {
6338 // CALLER: RedAnd, RedOr, ...
6339 // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
6340 // IEEE-2012: Table 11-21 and 11.8.1:
6341 // LHS is self-determined
6342 // Width: 1 bit out
6343 // Sign: unsigned out (11.8.1)
6344
2/2
✓ Branch 0 taken 328 times.
✓ Branch 1 taken 273 times.
601 if (m_vup->prelim()) {
6345 328 iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
6346 328 nodep->dtypeSetBit();
6347 }
6348 601 }
6349 void visit_red_unknown(AstNodeUniop* nodep) {
6350 // CALLER: IsUnknown
6351 // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE)
6352 // IEEE-2012: Table 11-21 and 11.8.1:
6353 // LHS is self-determined
6354 // Width: 1 bit out
6355 // Sign: unsigned out (11.8.1)
6356 if (m_vup->prelim()) {
6357 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6358 nodep->dtypeSetBit();
6359 }
6360 }
6361
6362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
1020 void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) {
6363 // CALLER: AstEq, AstGt, ..., AstLtS
6364 // Real allowed if and only if real_lhs set
6365 // See IEEE-2012 11.4.4, and 11.8.1:
6366 // Widths: 1 bit out, width is max of LHS or RHS
6367 // Sign: signed compare (not output) if both signed, compare is signed,
6368 // width mismatches sign extend
6369 // else, compare is unsigned, **zero-extends**
6370 // Real: If either real, other side becomes real and real compare
6371 // TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq
6372 // TODO: chandle/class handle/iface handle only allowed to self-compare or against null
6373 // TODO: chandle/class handle/iface handle no relational compares
6374
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
1020 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
6375
2/2
✓ Branch 0 taken 510 times.
✓ Branch 1 taken 510 times.
1020 if (m_vup->prelim()) {
6376 510 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6377 510 userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6378
2/4
✓ Branch 1 taken 510 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 510 times.
510 if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
6379 if (!realok) {
6380 nodep->v3error("Real is illegal operand to ?== operator");
6381 AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
6382 nodep->replaceWith(newp);
6383 VL_DO_DANGLING(pushDeletep(nodep), nodep);
6384 return;
6385 }
6386 if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
6387 VL_DANGLING(nodep);
6388 nodep = newp; // Process new node instead
6389 iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
6390 iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
6391 }
6392
2/4
✓ Branch 1 taken 510 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 510 times.
✗ Branch 5 not taken.
510 } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) {
6393 if (AstNodeBiop* const newp = replaceWithNVersion(nodep)) {
6394 VL_DANGLING(nodep);
6395 nodep = newp; // Process new node instead
6396 iterateCheckString(nodep, "LHS", nodep->lhsp(), FINAL);
6397 iterateCheckString(nodep, "RHS", nodep->rhsp(), FINAL);
6398 }
6399 } else {
6400 const bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned();
6401
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 510 times.
510 if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, signedFl)) {
6402 VL_DANGLING(nodep);
6403 nodep = newp; // Process new node instead
6404 }
6405
3/6
✓ Branch 0 taken 510 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 510 times.
✓ Branch 4 taken 510 times.
✗ Branch 5 not taken.
1020 const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
6406
2/4
✓ Branch 0 taken 510 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 510 times.
1020 const int ewidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
6407 AstNodeDType* const subDTypep
6408
1/2
✓ Branch 0 taken 510 times.
✗ Branch 1 not taken.
1020 = nodep->findLogicDType(width, ewidth, VSigning::fromBool(signedFl));
6409 bool warnOn = true;
6410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 510 times.
510 if (!signedFl && width == 32) {
6411 // Waive on unsigned < or <= if RHS is narrower, since can't give wrong answer
6412 if ((VN_IS(nodep, Lt) || VN_IS(nodep, Lte))
6413 && (nodep->lhsp()->width() >= nodep->rhsp()->widthMin())) {
6414 warnOn = false;
6415 }
6416 // Waive on unsigned > or >= if RHS is wider, since can't give wrong answer
6417 if ((VN_IS(nodep, Gt) || VN_IS(nodep, Gte))
6418 && (nodep->lhsp()->widthMin() >= nodep->rhsp()->width())) {
6419 warnOn = false;
6420 }
6421 }
6422
1/2
✓ Branch 0 taken 510 times.
✗ Branch 1 not taken.
1020 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep,
6423 (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
6424 510 iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep,
6425 (signedFl ? EXTEND_LHS : EXTEND_ZERO), warnOn);
6426 }
6427 510 nodep->dtypeSetBit();
6428 }
6429 }
6430 void visit_cmp_real(AstNodeBiop* nodep) {
6431 // CALLER: EqD, LtD
6432 // Widths: 1 bit out, lhs width == rhs width
6433 // Signed compare (not output) if both sides signed
6434 // Real if and only if real_allow set
6435 // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use
6436 // "zero padding" on unsigned
6437 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
6438 if (m_vup->prelim()) {
6439 // See similar handling in visit_cmp_eq_gt where created
6440 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6441 iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
6442 nodep->dtypeSetBit();
6443 }
6444 }
6445 void visit_cmp_string(AstNodeBiop* nodep) {
6446 // CALLER: EqN, LtN
6447 // Widths: 1 bit out, lhs width == rhs width
6448 // String compare (not output)
6449 // Real if and only if real_allow set
6450 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
6451 if (m_vup->prelim()) {
6452 // See similar handling in visit_cmp_eq_gt where created
6453 iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
6454 iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH);
6455 nodep->dtypeSetBit();
6456 }
6457 }
6458 void visit_cmp_type(AstNodeBiop* nodep) {
6459 // CALLER: EqT, LtT
6460 // Widths: 1 bit out
6461 // Data type compare (not output)
6462 if (m_vup->prelim()) {
6463 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
6464 userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
6465 const AstAttrOf* const lhsap = VN_AS(nodep->lhsp(), AttrOf);
6466 const AstAttrOf* const rhsap = VN_AS(nodep->rhsp(), AttrOf);
6467 UASSERT_OBJ(lhsap->attrType() == VAttrType::TYPEID, lhsap,
6468 "Type compare expects type reference");
6469 UASSERT_OBJ(rhsap->attrType() == VAttrType::TYPEID, rhsap,
6470 "Type compare expects type reference");
6471 AstNodeDType* const lhsDtp = VN_AS(lhsap->fromp(), NodeDType);
6472 AstNodeDType* const rhsDtp = VN_AS(rhsap->fromp(), NodeDType);
6473 UINFO(9, "==type lhsDtp " << lhsDtp << endl);
6474 UINFO(9, "==type rhsDtp " << lhsDtp << endl);
6475 const bool invert = VN_IS(nodep, NeqT);
6476 const bool identical
6477 = AstNode::computeCastable(lhsDtp, rhsDtp, nodep) == VCastable::SAMEISH;
6478 UINFO(9, "== " << identical << endl);
6479 const bool eq = invert ^ identical;
6480 AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitTrue{}, eq};
6481 nodep->replaceWith(newp);
6482 VL_DO_DANGLING(pushDeletep(nodep), nodep);
6483 }
6484 }
6485
6486
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1696 times.
1696 void visit_negate_not(AstNodeUniop* nodep, bool real_ok) {
6487 // CALLER: (real_ok=false) Not
6488 // CALLER: (real_ok=true) Negate - allow real numbers
6489 // Signed: From lhs
6490 // IEEE-2012 Table 11-21:
6491 // Widths: out width = lhs width
6492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1696 times.
1696 UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
6493
2/2
✓ Branch 0 taken 848 times.
✓ Branch 1 taken 848 times.
1696 if (m_vup->prelim()) {
6494 848 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6495
2/2
✓ Branch 0 taken 796 times.
✓ Branch 1 taken 52 times.
848 if (!real_ok) checkCvtUS(nodep->lhsp());
6496 }
6497
3/4
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 1592 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 104 times.
1696 if (real_ok && nodep->lhsp()->isDouble()) {
6498 spliceCvtD(nodep->lhsp());
6499 if (AstNodeUniop* const newp = replaceWithDVersion(nodep)) {
6500 VL_DANGLING(nodep);
6501 nodep = newp; // Process new node instead
6502 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6503 nodep->dtypeSetDouble();
6504 return;
6505 }
6506 } else {
6507 // Note there aren't yet uniops that need version changes
6508 // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned())
6509 }
6510
2/2
✓ Branch 0 taken 848 times.
✓ Branch 1 taken 848 times.
1696 if (m_vup->prelim()) nodep->dtypeFrom(nodep->lhsp());
6511
2/2
✓ Branch 0 taken 848 times.
✓ Branch 1 taken 848 times.
1696 if (m_vup->final()) {
6512 848 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
6513 nodep->dtypep(expDTypep); // Propagate expression type to negation
6514 AstNodeDType* const subDTypep = expDTypep;
6515 // Some warning suppressions
6516 bool lhsWarn = true;
6517 if (VN_IS(nodep, Negate)) {
6518 // Warn if user wants extra bit from carry
6519
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
6520 }
6521 848 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
6522 lhsWarn);
6523 }
6524 }
6525
6526 void visit_signed_unsigned(AstNodeUniop* nodep, VSigning rs_out) {
6527 // CALLER: Signed, Unsigned
6528 // Width: lhs is self determined width
6529 // See IEEE-2012 6.24.1:
6530 // Width: Returns packed array, of size $bits(expression).
6531 // Sign: Output sign is as specified by operation
6532 // TODO: Type: Two-state if input is two-state, else four-state
6533 UASSERT_OBJ(!nodep->op2p(), nodep, "For unary ops only!");
6534 if (m_vup->prelim()) {
6535 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
6536 checkCvtUS(nodep->lhsp());
6537 const int width = nodep->lhsp()->width();
6538 AstNodeDType* const expDTypep = nodep->findLogicDType(width, width, rs_out);
6539 nodep->dtypep(expDTypep);
6540 AstNodeDType* const subDTypep = expDTypep;
6541 // The child's width is self determined
6542 iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP);
6543 }
6544 }
6545
6546 698 void visit_shift(AstNodeBiop* nodep) {
6547 // CALLER: ShiftL, ShiftR, ShiftRS
6548 // Widths: Output width from lhs, rhs<33 bits
6549 // Signed: Output signed iff LHS signed; unary operator
6550 // See IEEE 2012 11.4.10:
6551 // RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
6552 698 iterate_shift_prelim(nodep);
6553
1/2
✓ Branch 0 taken 698 times.
✗ Branch 1 not taken.
1396 nodep->dtypeChgSigned(nodep->lhsp()->isSigned());
6554 698 const AstNodeBiop* const newp = iterate_shift_final(nodep);
6555 VL_DANGLING(nodep);
6556 (void)newp; // Ununused
6557 698 }
6558 698 void iterate_shift_prelim(AstNodeBiop* nodep) {
6559 // Shifts
6560 // See IEEE-2012 11.4.10 and Table 11-21.
6561 // RHS is self-determined. RHS is always treated as unsigned, has no effect on result.
6562
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 349 times.
698 if (m_vup->prelim()) {
6563 349 userIterateAndNext(nodep->lhsp(), WidthVP{SELF, PRELIM}.p());
6564 349 checkCvtUS(nodep->lhsp());
6565 349 iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
6566 nodep->dtypeFrom(nodep->lhsp());
6567 }
6568 698 }
6569 698 AstNodeBiop* iterate_shift_final(AstNodeBiop* nodep) {
6570 // Nodep maybe edited
6571
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 349 times.
698 if (m_vup->final()) {
6572 349 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
6573 AstNodeDType* const subDTypep = expDTypep;
6574 nodep->dtypep(expDTypep);
6575 // ShiftRS converts to ShiftR, but not vice-versa
6576 if (VN_IS(nodep, ShiftRS)) {
6577
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 79 times.
79 if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, nodep->isSigned())) {
6578 VL_DANGLING(nodep);
6579 nodep = newp; // Process new node instead
6580 }
6581 }
6582 bool warnOn = true;
6583 // No warning if "X = 1'b1<<N"; assume user is doing what they want
6584
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 349 times.
349 if (nodep->lhsp()->isOne() && VN_IS(nodep->backp(), NodeAssign)) warnOn = false;
6585 // We don't currently suppress these, as it's the upper operator (e.g. assign)
6586 // that reports the WIDTHEXPAND.
6587 AstConst* const shiftp = VN_CAST(nodep->rhsp(), Const);
6588 if (shiftp && !shiftp->num().isFourState() && shiftp->width() <= 32) {
6589 const int64_t shiftVal = shiftp->num().toSQuad();
6590 if (VN_IS(nodep, ShiftL)) {
6591 if (shiftVal > 0 && nodep->width() == nodep->lhsp()->width() + shiftVal)
6592 warnOn = false;
6593 }
6594 }
6595 349 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
6596 warnOn);
6597
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 339 times.
349 if (nodep->rhsp()->width() > 32) {
6598
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
10 if (shiftp && shiftp->num().mostSetBitP1() <= 32) {
6599 // If (number)<<96'h1, then make it into (number)<<32'h1
6600 V3Number num(shiftp, 32, 0);
6601 num.opAssign(shiftp->num());
6602 AstNode* const shiftrhsp = nodep->rhsp();
6603 nodep->rhsp()->replaceWith(new AstConst{shiftrhsp->fileline(), num});
6604 VL_DO_DANGLING(shiftrhsp->deleteTree(), shiftrhsp);
6605 }
6606 }
6607 }
6608 698 return nodep; // May edit
6609 }
6610
6611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2950 times.
2950 void visit_boolexpr_and_or(AstNodeBiop* nodep) {
6612 // CALLER: And, Or, Xor, ...
6613 // Lint widths: out width = lhs width = rhs width
6614 // Signed: if lhs & rhs signed
6615 // IEEE-2012 Table 11-21:
6616 // Width: max(LHS, RHS)
6617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2950 times.
2950 UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
6618 // If errors are off, we need to follow the spec; thus we really need to do the max()
6619 // because the rhs could be larger, and we need to have proper editing to get the widths
6620 // to be the same for our operations.
6621
2/2
✓ Branch 0 taken 1475 times.
✓ Branch 1 taken 1475 times.
2950 if (m_vup->prelim()) { // First stage evaluation
6622 // Determine expression widths only relying on what's in the subops
6623 1475 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6624 1475 userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6625 1475 checkCvtUS(nodep->lhsp());
6626 1475 checkCvtUS(nodep->rhsp());
6627
3/6
✓ Branch 0 taken 1475 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1475 times.
✓ Branch 4 taken 1475 times.
✗ Branch 5 not taken.
2950 const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
6628
3/6
✓ Branch 0 taken 1475 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1475 times.
✓ Branch 4 taken 1475 times.
✗ Branch 5 not taken.
2950 const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
6629 const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
6630 1475 nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
6631 }
6632
2/2
✓ Branch 0 taken 1475 times.
✓ Branch 1 taken 1475 times.
2950 if (m_vup->final()) {
6633 1475 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
6634 AstNodeDType* const subDTypep = expDTypep;
6635 nodep->dtypep(expDTypep);
6636 // Error report and change sizes for suboperands of this node.
6637 1475 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
6638 1475 iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
6639 }
6640 2950 }
6641
6642 912 void visit_add_sub_replace(AstNodeBiop* nodep, bool real_ok) {
6643 // CALLER: (real_ok=false) AddS, SubS, ...
6644 // CALLER: (real_ok=true) Add, Sub, ...
6645 // Widths: out width = lhs width = rhs width
6646 // Signed: Replace operator with signed operator, or signed to unsigned
6647 // Real: Replace operator with real operator
6648 // IEEE-2012 Table 11-21:
6649 // Width: max(LHS, RHS)
6650 // If errors are off, we need to follow the spec; thus we really need to do the max()
6651 // because the rhs could be larger, and we need to have proper editing to get the widths
6652 // to be the same for our operations.
6653 //
6654 // if (debug() >= 9) { UINFO(0,"rus "<<m_vup<<endl); nodep->dumpTree("- rusin: "); }
6655
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 456 times.
912 if (m_vup->prelim()) { // First stage evaluation
6656 // Determine expression widths only relying on what's in the subops
6657 456 userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6658 456 userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
6659
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 328 times.
456 if (!real_ok) {
6660 128 checkCvtUS(nodep->lhsp());
6661 128 checkCvtUS(nodep->rhsp());
6662 }
6663
2/4
✓ Branch 1 taken 456 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 456 times.
456 if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) {
6664 spliceCvtD(nodep->lhsp());
6665 spliceCvtD(nodep->rhsp());
6666 if (AstNodeBiop* const newp = replaceWithDVersion(nodep)) {
6667 VL_DANGLING(nodep);
6668 nodep = newp; // Process new node instead
6669 }
6670 nodep->dtypeSetDouble();
6671 iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL);
6672 iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL);
6673 return;
6674 } else {
6675
3/6
✓ Branch 0 taken 456 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 456 times.
✓ Branch 4 taken 456 times.
✗ Branch 5 not taken.
912 const int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width());
6676
3/6
✓ Branch 0 taken 456 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 456 times.
✓ Branch 4 taken 456 times.
✗ Branch 5 not taken.
912 const int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin());
6677 const bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
6678 456 nodep->dtypeChgWidthSigned(width, mwidth, VSigning::fromBool(expSigned));
6679 }
6680 }
6681
2/2
✓ Branch 0 taken 456 times.
✓ Branch 1 taken 456 times.
912 if (m_vup->final()) {
6682 // Parent's data type was computed using the max(upper, nodep->dtype)
6683 456 AstNodeDType* const expDTypep = m_vup->dtypeOverridep(nodep->dtypep());
6684 AstNodeDType* const subDTypep = expDTypep;
6685 nodep->dtypep(expDTypep);
6686 // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test
6687 // bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned());
6688
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 456 times.
456 if (AstNodeBiop* const newp = replaceWithUOrSVersion(nodep, expDTypep->isSigned())) {
6689 VL_DANGLING(nodep);
6690 nodep = newp; // Process new node instead
6691 }
6692 // Some warning suppressions
6693 bool lhsWarn = true;
6694 bool rhsWarn = true;
6695 if (VN_IS(nodep, Add) || VN_IS(nodep, Sub)) {
6696 // Warn if user wants extra bit from carry
6697
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (subDTypep->widthMin() == (nodep->lhsp()->widthMin() + 1)) lhsWarn = false;
6698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 if (subDTypep->widthMin() == (nodep->rhsp()->widthMin() + 1)) rhsWarn = false;
6699 } else if (VN_IS(nodep, Mul) || VN_IS(nodep, MulS)) {
6700
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn = false;
6701
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn = false;
6702 }
6703 // Final call, so make sure children check their sizes
6704 // Error report and change sizes for suboperands of this node.
6705 456 iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
6706 lhsWarn);
6707 456 iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP,
6708 rhsWarn);
6709 }
6710 // if (debug() >= 9) nodep->dumpTree("- rusou: ");
6711 }
6712 void visit_real_add_sub(AstNodeBiop* nodep) {
6713 // CALLER: AddD, MulD, ...
6714 if (m_vup->prelim()) { // First stage evaluation
6715 // Note similar steps in visit_add_sub_replace promotion to double
6716 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6717 iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH);
6718 nodep->dtypeSetDouble();
6719 }
6720 }
6721 void visit_real_neg_ceil(AstNodeUniop* nodep) {
6722 // CALLER: Negate, Ceil, Log, ...
6723 if (m_vup->prelim()) { // First stage evaluation
6724 // See alsl visit_negate_not conversion
6725 iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH);
6726 nodep->dtypeSetDouble();
6727 }
6728 }
6729
6730 //----------------------------------------------------------------------
6731 // LOWER LEVEL WIDTH METHODS (none iterate)
6732
6733
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 21030 times.
21085 bool widthBad(AstNode* nodep, AstNodeDType* expDTypep) {
6734 const int expWidth = expDTypep->width();
6735 int expWidthMin = expDTypep->widthMin();
6736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21085 times.
21085 UASSERT_OBJ(nodep->dtypep(), nodep,
6737 "Under node " << nodep->prettyTypeName()
6738 << " has no dtype?? Missing Visitor func?");
6739
2/4
✓ Branch 1 taken 21085 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 21085 times.
21085 if (expDTypep->basicp()->untyped() || nodep->dtypep()->basicp()->untyped()) return false;
6740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21085 times.
21085 UASSERT_OBJ(nodep->width() != 0, nodep,
6741 "Under node " << nodep->prettyTypeName()
6742 << " has no expected width?? Missing Visitor func?");
6743
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21085 times.
21085 UASSERT_OBJ(expWidth != 0, nodep,
6744 "Node " << nodep->prettyTypeName()
6745 << " has no expected width?? Missing Visitor func?");
6746
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21085 times.
21085 if (expWidthMin == 0) expWidthMin = expWidth;
6747
2/2
✓ Branch 0 taken 8635 times.
✓ Branch 1 taken 12450 times.
21085 if (nodep->dtypep()->width() == expWidth) return false;
6748
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true;
6749
1/2
✓ Branch 0 taken 12184 times.
✗ Branch 1 not taken.
12184 if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true;
6750 return false;
6751 }
6752
6753 6092 void fixWidthExtend(AstNodeExpr* nodep, AstNodeDType* expDTypep, ExtendRule extendRule) {
6754 // Fix the width mismatch by extending or truncating bits
6755 // *ONLY* call this from checkWidth()
6756 // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312;
6757 // A(CONSTwide)+B becomes A(CONSTwidened)+B
6758 // A(somewide)+B becomes A(TRUNC(somewide,width))+B
6759 // or A(EXTRACT(somewide,width,0))+B
6760 // Sign extension depends on the type of the *present*
6761 // node, while the output dtype is the *expected* sign.
6762 // It is reasonable to have sign extension with unsigned output,
6763 // for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out
6764
1/8
✗ Branch 1 not taken.
✓ Branch 2 taken 6092 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
6092 UINFO(4,
6765 " widthExtend_(r=" << static_cast<int>(extendRule) << ") old: " << nodep << endl);
6766
1/2
✓ Branch 0 taken 6092 times.
✗ Branch 1 not taken.
6092 if (extendRule == EXTEND_OFF) return;
6767 AstConst* const constp = VN_CAST(nodep, Const);
6768 const int expWidth = expDTypep->width();
6769 if (constp && !constp->num().isNegative()) {
6770 // Save later constant propagation work, just right-size it.
6771
1/2
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
6092 V3Number num(nodep, expWidth);
6772
1/2
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
6092 num.opAssign(constp->num());
6773 num.isSigned(false);
6774
2/4
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6092 times.
✗ Branch 5 not taken.
6092 AstNodeExpr* const newp = new AstConst{nodep->fileline(), num};
6775
1/2
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
6092 constp->replaceWith(newp);
6776 VL_DO_DANGLING(pushDeletep(constp), constp);
6777 VL_DANGLING(nodep);
6778 nodep = newp;
6779 } else if (expWidth < nodep->width()) {
6780 // Trunc - Extract
6781 VNRelinker linker;
6782 nodep->unlinkFrBack(&linker);
6783 AstNodeExpr* const newp = new AstSel{nodep->fileline(), nodep, 0, expWidth};
6784 newp->didWidth(true); // Don't replace dtype with unsigned
6785 linker.relink(newp);
6786 nodep = newp;
6787 } else {
6788 // Extend
6789 VNRelinker linker;
6790 nodep->unlinkFrBack(&linker);
6791 bool doSigned = false;
6792 switch (extendRule) {
6793 case EXTEND_ZERO: doSigned = false; break;
6794 case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break;
6795 case EXTEND_LHS: doSigned = nodep->isSigned(); break;
6796 default: nodep->v3fatalSrc("bad case");
6797 }
6798 AstNodeExpr* const newp
6799 = (doSigned ? static_cast<AstNodeExpr*>(new AstExtendS{nodep->fileline(), nodep})
6800 : static_cast<AstNodeExpr*>(new AstExtend{nodep->fileline(), nodep}));
6801 newp->didWidth(true);
6802 linker.relink(newp);
6803 nodep = newp;
6804 }
6805
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 6092 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6092 if (expDTypep->isDouble() && !nodep->isDouble()) {
6806 // For AstVar init() among others
6807 // TODO do all to-real and to-integer conversions in this function
6808 // rather than in callers
6809 AstNodeExpr* const newp = spliceCvtD(nodep);
6810 nodep = newp;
6811 }
6812 nodep->dtypep(expDTypep);
6813
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 6092 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
6092 UINFO(4, " _new: " << nodep << endl);
6814 }
6815
6816 266 void fixWidthReduce(AstNodeExpr* nodep) {
6817 // Fix the width mismatch by adding a reduction OR operator
6818 // IF (A(CONSTwide)) becomes IF (A(CONSTreduced))
6819 // IF (A(somewide)) becomes IF (A(REDOR(somewide)))
6820 // Attempt to fix it quietly
6821 const int expWidth = 1;
6822 const int expSigned = false;
6823
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
266 UINFO(4, " widthReduce_old: " << nodep << endl);
6824 AstConst* const constp = VN_CAST(nodep, Const);
6825 if (constp) {
6826 V3Number num(nodep, expWidth);
6827 num.opRedOr(constp->num());
6828 num.isSigned(expSigned);
6829 AstNodeExpr* const newp = new AstConst{nodep->fileline(), num};
6830 constp->replaceWith(newp);
6831 VL_DO_DANGLING(constp->deleteTree(), constp);
6832 VL_DANGLING(nodep);
6833 nodep = newp;
6834 } else {
6835
1/2
✓ Branch 1 taken 266 times.
✗ Branch 2 not taken.
266 VNRelinker linker;
6836 nodep->unlinkFrBack(&linker);
6837
2/4
✓ Branch 1 taken 266 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 266 times.
✗ Branch 5 not taken.
266 AstNodeExpr* const newp = new AstRedOr{nodep->fileline(), nodep};
6838 linker.relink(newp);
6839 nodep = newp;
6840 }
6841 266 nodep->dtypeChgWidthSigned(expWidth, expWidth, VSigning::fromBool(expSigned));
6842
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
266 UINFO(4, " _new: " << nodep << endl);
6843 266 }
6844
6845 6092 bool fixAutoExtend(AstNodeExpr*& nodepr, int expWidth) {
6846 // For SystemVerilog '0,'1,'x,'z, autoextend and don't warn
6847
1/2
✓ Branch 0 taken 6092 times.
✗ Branch 1 not taken.
6092 if (AstConst* const constp = VN_CAST(nodepr, Const)) {
6848
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6092 if (constp->num().autoExtend() && !constp->num().sized() && constp->width() == 1) {
6849 // Make it the proper size. Careful of proper extension of 0's/1's
6850 V3Number num(constp, expWidth);
6851 num.opRepl(constp->num(), expWidth); // {width{'1}}
6852 AstNodeExpr* const newp = new AstConst{constp->fileline(), num};
6853 // Spec says always unsigned with proper width
6854 if (debug() > 4) constp->dumpTree("- fixAutoExtend_old: ");
6855 if (debug() > 4) newp->dumpTree("- _new: ");
6856 constp->replaceWith(newp);
6857 VL_DO_DANGLING(constp->deleteTree(), constp);
6858 // Tell caller the new constp, and that we changed it.
6859 nodepr = newp;
6860 return true;
6861 }
6862 // X/Z also upper bit extend. In pre-SV only to 32-bits, SV forever.
6863 else if (!constp->num().sized()
6864 // Make it the proper size. Careful of proper extension of 0's/1's
6865
2/6
✓ Branch 0 taken 6092 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6092 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6092 && expWidth > 32 && constp->num().isMsbXZ()) {
6866 constp->v3warn(WIDTHXZEXPAND, "Unsized constant being X/Z extended to "
6867 << expWidth
6868 << " bits: " << constp->prettyName());
6869 V3Number num(constp, expWidth);
6870 num.opExtendXZ(constp->num(), constp->width());
6871 AstNodeExpr* const newp = new AstConst{constp->fileline(), num};
6872 // Spec says always unsigned with proper width
6873 if (debug() > 4) constp->dumpTree("- fixUnszExtend_old: ");
6874 if (debug() > 4) newp->dumpTree("- _new: ");
6875 constp->replaceWith(newp);
6876 VL_DO_DANGLING(constp->deleteTree(), constp);
6877 // Tell caller the new constp, and that we changed it.
6878 nodepr = newp;
6879 return true;
6880 }
6881 }
6882 return false; // No change
6883 }
6884 bool isBaseClassRecurse(const AstClass* const cls1p, const AstClass* const cls2p) {
6885 // Returns true if cls1p and cls2p are equal or if cls1p is a base class of cls2p
6886 if (cls1p == cls2p) return true;
6887 for (const AstClassExtends* cextp = cls2p->extendsp(); cextp;
6888 cextp = VN_CAST(cextp->nextp(), ClassExtends)) {
6889 if (isBaseClassRecurse(cls1p, cextp->classp())) return true;
6890 }
6891 return false;
6892 }
6893 3735 void checkClassAssign(AstNode* nodep, const char* side, AstNode* rhsp,
6894 AstNodeDType* const lhsDTypep) {
6895 if (AstClassRefDType* const lhsClassRefp = VN_CAST(lhsDTypep->skipRefp(), ClassRefDType)) {
6896 UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type");
6897 AstNodeDType* const rhsDtypep = rhsp->dtypep()->skipRefp();
6898 if (AstClassRefDType* const rhsClassRefp = VN_CAST(rhsDtypep, ClassRefDType)) {
6899 if (isBaseClassRecurse(lhsClassRefp->classp(), rhsClassRefp->classp())) return;
6900 } else if (rhsp->isNull()) {
6901 return;
6902 }
6903 nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got "
6904 << rhsDtypep->prettyTypeName());
6905 }
6906 }
6907 static bool similarDTypeRecurse(const AstNodeDType* const node1p,
6908 const AstNodeDType* const node2p) {
6909 return node1p->skipRefp()->similarDType(node2p->skipRefp());
6910 }
6911 void iterateCheckFileDesc(AstNode* nodep, AstNode* underp, Stage stage) {
6912 UASSERT_OBJ(stage == BOTH, nodep, "Bad call");
6913 // underp may change as a result of replacement
6914 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
6915 AstNodeDType* const expDTypep = underp->findUInt32DType();
6916 underp
6917 = iterateCheck(nodep, "file_descriptor", underp, SELF, FINAL, expDTypep, EXTEND_EXP);
6918 (void)underp; // cppcheck
6919 }
6920 void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
6921 // Coerce child to signed32 if not already. Child is self-determined
6922 // underp may change as a result of replacement
6923 if (stage & PRELIM) {
6924 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
6925 }
6926 if (stage & FINAL) {
6927 AstNodeDType* const expDTypep = nodep->findSigned32DType();
6928 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
6929 }
6930 (void)underp; // cppcheck
6931 }
6932 void iterateCheckDelay(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
6933 // Coerce child to 64-bit delay if not already. Child is self-determined
6934 // underp may change as a result of replacement
6935 if (stage & PRELIM) {
6936 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
6937 }
6938 if (stage & FINAL) {
6939 AstNodeDType* expDTypep;
6940 if (underp->dtypep()->skipRefp()->isDouble()) { // V3Timing will later convert double
6941 expDTypep = nodep->findDoubleDType();
6942 } else {
6943 FileLine* const newFl = new FileLine{underp->fileline()};
6944 newFl->warnOff(V3ErrorCode::WIDTHEXPAND, true);
6945 underp->fileline(newFl);
6946 expDTypep = nodep->findLogicDType(64, 64, VSigning::UNSIGNED);
6947 }
6948 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP, false);
6949 }
6950 (void)underp; // cppcheck
6951 }
6952 void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
6953 // Coerce child to real if not already. Child is self-determined
6954 // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST)
6955 // Don't need separate PRELIM and FINAL(double) calls;
6956 // as if resolves to double, the BOTH correctly resolved double,
6957 // otherwise self-determined was correct
6958 // underp may change as a result of replacement
6959 if (stage & PRELIM) {
6960 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
6961 }
6962 if (stage & FINAL) {
6963 AstNodeDType* const expDTypep = nodep->findDoubleDType();
6964 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
6965 }
6966 (void)underp; // cppcheck
6967 }
6968 void iterateCheckString(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
6969 AstNodeDType* const expDTypep = nodep->findStringDType();
6970 if (stage & PRELIM) {
6971 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, PRELIM}.p());
6972 }
6973 if (stage & FINAL) {
6974 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
6975 }
6976 (void)underp; // cppcheck
6977 }
6978 void iterateCheckTyped(AstNode* nodep, const char* side, AstNode* underp,
6979 AstNodeDType* expDTypep, Stage stage) {
6980 if (stage & PRELIM) {
6981 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, PRELIM}.p());
6982 }
6983 if (stage & FINAL) {
6984 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
6985 }
6986 (void)underp; // cppcheck
6987 }
6988 41929 void iterateCheckSizedSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ,
6989 Stage stage) {
6990 // Coerce child to any sized-number data type; child is self-determined
6991 // i.e. isolated from expected type.
6992 // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs)
6993
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41929 times.
41929 UASSERT_OBJ(determ == SELF, nodep, "Bad call");
6994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41929 times.
41929 UASSERT_OBJ(stage == FINAL || stage == BOTH, nodep, "Bad call");
6995 // underp may change as a result of replacement
6996
1/2
✓ Branch 0 taken 41929 times.
✗ Branch 1 not taken.
41929 if (stage & PRELIM) {
6997 83858 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, PRELIM}.p());
6998 }
6999 41929 underp = VN_IS(underp, NodeExpr) ? checkCvtUS(VN_AS(underp, NodeExpr)) : underp;
7000 AstNodeDType* const expDTypep = underp->dtypep();
7001 41929 underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP);
7002 (void)underp; // cppcheck
7003 41929 }
7004 3735 void iterateCheckAssign(AstNode* nodep, const char* side, AstNode* rhsp, Stage stage,
7005 AstNodeDType* lhsDTypep) {
7006 // Check using assignment-like context rules
7007 // if (debug()) nodep->dumpTree("- checkass: ");
7008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3735 times.
3735 UASSERT_OBJ(stage == FINAL, nodep, "Bad width call");
7009 // Create unpacked byte from string see IEEE 1800-2023 5.9
7010 if (AstConst* constp = VN_CAST(rhsp, Const)) {
7011 if (const AstUnpackArrayDType* const arrayp
7012 = VN_CAST(lhsDTypep->skipRefp(), UnpackArrayDType)) {
7013 if (AstBasicDType* basicp = VN_CAST(arrayp->subDTypep()->skipRefp(), BasicDType)) {
7014 if (basicp->width() == 8 && constp->num().isFromString()) {
7015 AstInitArray* newp = new AstInitArray{
7016 constp->fileline(), lhsDTypep,
7017 new AstConst{constp->fileline(), AstConst::WidthedValue{}, 8, 0}};
7018 for (int aindex = arrayp->lo(); aindex <= arrayp->hi(); ++aindex) {
7019 int cindex = arrayp->declRange().ascending() ? (arrayp->hi() - aindex)
7020 : (aindex - arrayp->lo());
7021 V3Number selected{constp, 8};
7022 selected.opSel(constp->num(), cindex * 8 + 7, cindex * 8);
7023 UINFO(0, " aindex=" << aindex << " cindex=" << cindex
7024 << " c=" << selected << endl);
7025 if (!selected.isFourState()) {
7026 if (const uint32_t c = selected.toUInt()) {
7027 newp->addIndexValuep(
7028 aindex, new AstConst{constp->fileline(),
7029 AstConst::WidthedValue{}, 8, c});
7030 }
7031 }
7032 }
7033 UINFO(6, " unpackFromString: " << nodep << endl);
7034 rhsp->replaceWith(newp);
7035 VL_DO_DANGLING(pushDeletep(rhsp), rhsp);
7036 rhsp = newp;
7037 }
7038 }
7039 }
7040 }
7041 // We iterate and size the RHS based on the result of RHS evaluation
7042 3735 checkClassAssign(nodep, side, rhsp, lhsDTypep);
7043 const bool lhsStream
7044 3735 = (VN_IS(nodep, NodeAssign) && VN_IS(VN_AS(nodep, NodeAssign)->lhsp(), NodeStream));
7045 3735 rhsp = iterateCheck(nodep, side, rhsp, ASSIGN, FINAL, lhsDTypep,
7046 lhsStream ? EXTEND_OFF : EXTEND_LHS);
7047 // if (debug()) nodep->dumpTree("- checkout: ");
7048 (void)rhsp; // cppcheck
7049 3735 }
7050
7051 944 void iterateCheckBool(AstNode* nodep, const char* side, AstNode* underp, Stage stage) {
7052
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 944 times.
944 UASSERT_OBJ(stage == BOTH, nodep,
7053 "Bad call"); // Booleans always self-determined so do BOTH at once
7054 // Underp is used in a self-determined but boolean context, reduce a
7055 // multibit number to one bit
7056 // stage is always BOTH so not passed as argument
7057 // underp may change as a result of replacement
7058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 944 times.
944 UASSERT_OBJ(underp, nodep, "Node has no type");
7059 944 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, BOTH}.p());
7060
2/4
✓ Branch 0 taken 944 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 944 times.
944 UASSERT_OBJ(underp && underp->dtypep(), nodep,
7061 "Node has no type"); // Perhaps forgot to do a prelim visit on it?
7062 //
7063 // For DOUBLE under a logical op, add implied test against zero, never a warning
7064 AstNodeDType* const underVDTypep = underp ? underp->dtypep()->skipRefp() : nullptr;
7065
1/2
✓ Branch 1 taken 944 times.
✗ Branch 2 not taken.
944 if (underp && underVDTypep->isDouble()) {
7066 UINFO(6, " spliceCvtCmpD0: " << underp << endl);
7067 VNRelinker linker;
7068 underp->unlinkFrBack(&linker);
7069 AstNode* const newp
7070 = new AstNeqD{nodep->fileline(), VN_AS(underp, NodeExpr),
7071 new AstConst{nodep->fileline(), AstConst::RealDouble{}, 0.0}};
7072 linker.relink(newp);
7073 } else if (VN_IS(underVDTypep, ClassRefDType) || VN_IS(underVDTypep, IfaceRefDType)
7074 || (VN_IS(underVDTypep, BasicDType)
7075
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 944 times.
944 && VN_AS(underVDTypep, BasicDType)->keyword() == VBasicDTypeKwd::CHANDLE)) {
7076 // Allow warning-free "if (handle)"
7077 VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed
7078
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 944 times.
944 } else if (!underVDTypep->basicp()) {
7079 nodep->v3error("Logical operator " << nodep->prettyTypeName()
7080 << " expects a non-complex data type on the "
7081 << side << ".");
7082 underp->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
7083 VL_DO_DANGLING(pushDeletep(underp), underp);
7084 } else {
7085 944 const bool bad = widthBad(underp, nodep->findBitDType());
7086
2/2
✓ Branch 0 taken 266 times.
✓ Branch 1 taken 678 times.
944 if (bad) {
7087 { // if (warnOn), but not needed here
7088
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 266 times.
266 if (debug() > 4) nodep->backp()->dumpTree("- back: ");
7089
12/32
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 9 taken 266 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 266 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 266 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 266 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 266 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 26 taken 266 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 266 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✓ Branch 32 taken 266 times.
✓ Branch 33 taken 10 times.
✓ Branch 34 taken 256 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 266 times.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
2138 nodep->v3widthWarn(1, underp->width(),
7090 "Logical operator "
7091 << nodep->prettyTypeName() << " expects 1 bit on the "
7092 << side << ", but " << side << "'s "
7093 << underp->prettyTypeName() << " generates "
7094 << underp->width()
7095 << (underp->width() != underp->widthMin()
7096 ? " or " + cvtToStr(underp->widthMin())
7097 : "")
7098 << " bits.");
7099 }
7100 266 VL_DO_DANGLING(fixWidthReduce(VN_AS(underp, NodeExpr)), underp); // Changed
7101 }
7102 }
7103 944 }
7104
7105 52113 AstNode* iterateCheck(AstNode* nodep, const char* side, AstNode* underp, Determ determ,
7106 Stage stage, AstNodeDType* expDTypep, ExtendRule extendRule,
7107 bool warnOn = true) {
7108 // Perform data type check on underp, which is underneath nodep used for error reporting
7109 // Returns the new underp
7110 // Conversion to/from doubles and integers are before iterating.
7111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52113 times.
52113 UASSERT_OBJ(stage == FINAL, nodep, "Bad state to iterateCheck");
7112
2/4
✓ Branch 0 taken 52113 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52113 times.
52113 UASSERT_OBJ(underp && underp->dtypep(), nodep,
7113 "Node has no type"); // Perhaps forgot to do a prelim visit on it?
7114 if (VN_IS(underp, NodeDType)) { // Note the node itself, not node's data type
7115 // Must be near top of these checks as underp->dtypep() will look normal
7116 underp->v3error(ucfirst(nodep->prettyOperatorName())
7117 << " expected non-datatype " << side << " but "
7118 << underp->prettyNameQ() << " is a datatype.");
7119
2/2
✓ Branch 0 taken 43193 times.
✓ Branch 1 taken 8920 times.
52113 } else if (expDTypep == underp->dtypep()) { // Perfect
7120 43193 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
7121
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8920 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8920 } else if (expDTypep->isDouble() && underp->isDouble()) { // Also good
7122 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
7123
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8920 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8920 } else if (expDTypep->isDouble() && !underp->isDouble()) {
7124 AstNode* const oldp
7125 = underp; // Need FINAL on children; otherwise splice would block it
7126 spliceCvtD(VN_AS(underp, NodeExpr));
7127 underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
7128
2/4
✓ Branch 1 taken 8920 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8920 times.
✗ Branch 5 not taken.
8920 } else if (!expDTypep->isDouble() && underp->isDouble()) {
7129 AstNode* const oldp
7130 = underp; // Need FINAL on children; otherwise splice would block it
7131 spliceCvtS(VN_AS(underp, NodeExpr), true, expDTypep->width()); // Round RHS
7132 underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
7133
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 8920 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
8920 } else if (expDTypep->isString() && !underp->dtypep()->isString()) {
7134 AstNode* const oldp
7135 = underp; // Need FINAL on children; otherwise splice would block it
7136 spliceCvtString(VN_AS(underp, NodeExpr));
7137 underp = userIterateSubtreeReturnEdits(oldp, WidthVP{SELF, FINAL}.p());
7138 } else {
7139 8920 const AstBasicDType* const expBasicp = expDTypep->basicp();
7140 8920 const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
7141
1/2
✓ Branch 0 taken 8920 times.
✗ Branch 1 not taken.
8920 if (expBasicp && underBasicp) {
7142 if (const AstEnumDType* const expEnump
7143 = VN_CAST(expDTypep->skipRefToEnump(), EnumDType)) {
7144 const auto castable
7145 = AstNode::computeCastable(expEnump, underp->dtypep(), underp);
7146 if (castable != VCastable::SAMEISH && castable != VCastable::COMPATIBLE
7147 && castable != VCastable::ENUM_IMPLICIT && !VN_IS(underp, Cast)
7148 && !VN_IS(underp, CastDynamic) && !m_enumItemp
7149 && !nodep->fileline()->warnIsOff(V3ErrorCode::ENUMVALUE) && warnOn) {
7150 underp->v3warn(ENUMVALUE,
7151 "Implicit conversion to enum "
7152 << expDTypep->prettyDTypeNameQ() << " from "
7153 << underp->dtypep()->prettyDTypeNameQ()
7154 << " (IEEE 1800-2023 6.19.3)\n"
7155 << nodep->warnMore()
7156 << "... Suggest use enum's mnemonic, or static cast");
7157 // if (debug()) nodep->backp()->dumpTree("- back: ");
7158 }
7159 }
7160 AstNodeDType* subDTypep = expDTypep;
7161 // We then iterate FINAL before width fixes, as if the under-operation
7162 // is e.g. an ADD, the ADD will auto-adjust to the proper data type
7163 // or if another operation e.g. ATOI will not.
7164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8920 times.
8920 if (determ == SELF) {
7165 underp = userIterateSubtreeReturnEdits(underp, WidthVP{SELF, FINAL}.p());
7166
2/2
✓ Branch 0 taken 3578 times.
✓ Branch 1 taken 5342 times.
8920 } else if (determ == ASSIGN) {
7167 // IEEE: Signedness is solely determined by the RHS
7168 // (underp), not by the LHS (expDTypep)
7169 if (underp->isSigned() != subDTypep->isSigned()
7170
2/4
✓ Branch 0 taken 3578 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3578 times.
7156 || underp->width() != subDTypep->width()) {
7171 subDTypep = nodep->findLogicDType(
7172 std::max(subDTypep->width(), underp->width()),
7173 std::max(subDTypep->widthMin(), underp->widthMin()),
7174 VSigning::fromBool(underp->isSigned()));
7175 UINFO(9, "Assignment of opposite-signed RHS to LHS: " << nodep << endl);
7176 }
7177 3578 underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p());
7178 } else {
7179 5342 underp = userIterateSubtreeReturnEdits(underp, WidthVP{subDTypep, FINAL}.p());
7180 }
7181 // Note the check uses the expected size, not the child's subDTypep as we want the
7182 // child node's width to end up correct for the assignment (etc)
7183 8920 widthCheckSized(nodep, side, VN_AS(underp, NodeExpr), expDTypep, extendRule,
7184 warnOn);
7185 } else if (!VN_IS(nodep, Eq) && !VN_IS(nodep, Neq)
7186 && !VN_IS(expDTypep->skipRefp(), IfaceRefDType)
7187 && VN_IS(underp->dtypep()->skipRefp(), IfaceRefDType)) {
7188 underp->v3error(ucfirst(nodep->prettyOperatorName())
7189 << " expected non-interface on " << side << " but "
7190 << underp->prettyNameQ() << " is an interface.");
7191 } else if (const AstIfaceRefDType* expIfaceRefp
7192 = VN_CAST(expDTypep->skipRefp(), IfaceRefDType)) {
7193 const AstIfaceRefDType* underIfaceRefp
7194 = VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType);
7195 if (!underIfaceRefp) {
7196 underp->v3error(ucfirst(nodep->prettyOperatorName())
7197 << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
7198 << " interface on " << side << " but " << underp->prettyNameQ()
7199 << " is not an interface.");
7200 } else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) {
7201 underp->v3error(ucfirst(nodep->prettyOperatorName())
7202 << " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
7203 << " interface on " << side << " but " << underp->prettyNameQ()
7204 << " is a different interface ("
7205 << underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ").");
7206 } else if (underIfaceRefp->modportp()
7207 && expIfaceRefp->modportp() != underIfaceRefp->modportp()) {
7208 underp->v3error(ucfirst(nodep->prettyOperatorName())
7209 << " expected "
7210 << (expIfaceRefp->modportp()
7211 ? expIfaceRefp->modportp()->prettyNameQ()
7212 : "no")
7213 << " interface modport on " << side << " but got "
7214 << underIfaceRefp->modportp()->prettyNameQ() << " modport.");
7215 } else {
7216 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
7217 }
7218 } else {
7219 // Hope it just works out (perhaps a cast will deal with it)
7220 underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
7221 }
7222 }
7223 52113 return underp;
7224 }
7225
7226 void
7227 17280 widthCheckSized(AstNode* nodep, const char* side,
7228 AstNodeExpr* underp, // Node to be checked or have typecast added in front of
7229 AstNodeDType* expDTypep, ExtendRule extendRule, bool warnOn = true) {
7230 // Issue warnings on sized number width mismatches, then do appropriate size extension
7231 // Generally iterateCheck is what is wanted instead of this
7232 // UINFO(9,"wchk "<<side<<endl<<" "<<nodep<<endl<<" "<<underp<<endl<<" e="<<expDTypep<<"
7233 // i"<<warnOn<<endl);
7234 17280 const AstBasicDType* const expBasicp = expDTypep->basicp();
7235 17280 const AstBasicDType* const underBasicp = underp->dtypep()->basicp();
7236
2/2
✓ Branch 0 taken 11781 times.
✓ Branch 1 taken 5499 times.
17280 if (expDTypep == underp->dtypep()) {
7237 return; // Same type must match
7238
2/4
✓ Branch 0 taken 11781 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11781 times.
✗ Branch 3 not taken.
11781 } else if (!expBasicp || expBasicp->isDouble() || !underBasicp
7239
2/4
✓ Branch 0 taken 11781 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11781 times.
23562 || underBasicp->isDouble()) {
7240 // This is perhaps a v3fatalSrc as we should have checked the types
7241 // before calling widthCheck, but we may have missed a non-sized
7242 // check in earlier code, so might as well assume it is the users'
7243 // fault.
7244 nodep->v3error(ucfirst(nodep->prettyOperatorName())
7245 << " expected non-complex non-double " << side << " in width check");
7246 #if VL_DEBUG
7247 nodep->v3fatalSrc("widthCheckSized should not be called on doubles/complex types");
7248 #endif
7249 return;
7250 } else {
7251 const int expWidth = expDTypep->width();
7252 11781 int expWidthMin = expDTypep->widthMin();
7253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11781 times.
11781 if (expWidthMin == 0) expWidthMin = expWidth;
7254 11781 const bool bad = widthBad(underp, expDTypep);
7255
4/6
✓ Branch 0 taken 11781 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6092 times.
✓ Branch 3 taken 5689 times.
✓ Branch 5 taken 6092 times.
✗ Branch 6 not taken.
23562 if ((bad || underp->width() != expWidth) && fixAutoExtend(underp /*ref*/, expWidth)) {
7256 underp = nullptr; // Changes underp
7257 return;
7258 }
7259 // If user has a sizing cast, assume they know what they are doing
7260 // (for better or worse)
7261 if (VN_IS(nodep->backp(), CastSize)) warnOn = false;
7262
2/4
✓ Branch 0 taken 11781 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 6273 times.
✗ Branch 4 not taken.
18054 if (VN_IS(underp, Const) && VN_AS(underp, Const)->num().isFromString()
7263 && expWidth > underp->width()
7264 && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized
7265 // reg [31:0] == "foo" we'll consider probably fine.
7266 // Maybe this should be a special warning? Not for now.
7267 warnOn = false;
7268 }
7269
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 78 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
78 if ((VN_IS(nodep, Add) && underp->width() == 1 && underp->isOne())
7270
1/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
82 || (VN_IS(nodep, Sub) && underp->width() == 1 && underp->isOne()
7271 && 0 == std::strcmp(side, "RHS"))) {
7272 // "foo + 1'b1", or "foo - 1'b1" are very common, people assume
7273 // they extend correctly
7274 warnOn = false;
7275 }
7276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11781 times.
11781 if (bad && warnOn) {
7277 if (debug() > 4) nodep->backp()->dumpTree("- back: ");
7278
7279 nodep->v3widthWarn(
7280 expWidth, underp->width(),
7281 ucfirst(nodep->prettyOperatorName())
7282 << " expects " << expWidth
7283 << (expWidth != expWidthMin ? " or " + cvtToStr(expWidthMin) : "")
7284 << " bits on the " << side << ", but " << side << "'s "
7285 << underp->prettyTypeName() << " generates " << underp->width()
7286 << (underp->width() != underp->widthMin()
7287 ? " or " + cvtToStr(underp->widthMin())
7288 : "")
7289 << " bits.");
7290 }
7291
4/6
✓ Branch 0 taken 11781 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11781 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6092 times.
✓ Branch 5 taken 5689 times.
23562 if (bad || underp->width() != expWidth) {
7292 // If we're in an NodeAssign, don't truncate the RHS if the LHS is
7293 // a NodeStream. The streaming operator changes the rules regarding
7294 // which bits to truncate.
7295 const AstNodeAssign* assignp = VN_CAST(nodep, NodeAssign);
7296 const AstPin* pinp = VN_CAST(nodep, Pin);
7297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6092 times.
6092 if (assignp && VN_IS(assignp->lhsp(), NodeStream)) {
7298
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6092 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6092 } else if (pinp && pinp->modVarp()->direction() != VDirection::INPUT) {
7299 // V3Inst::pinReconnectSimple must deal
7300 UINFO(5, "pinInSizeMismatch: " << pinp);
7301 } else {
7302 6092 VL_DO_DANGLING(fixWidthExtend(underp, expDTypep, extendRule), underp);
7303 }
7304 }
7305 }
7306 }
7307
7308 //----------------------------------------------------------------------
7309 // SIGNED/DOUBLE METHODS
7310
7311 54640 AstNodeExpr* checkCvtUS(AstNodeExpr* nodep) {
7312
2/4
✓ Branch 0 taken 54640 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 54640 times.
✗ Branch 4 not taken.
54640 if (nodep && nodep->isDouble()) {
7313 nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeNameQ()
7314 << ") input to "
7315 << nodep->backp()->prettyTypeName());
7316 nodep = spliceCvtS(nodep, true, 32);
7317 }
7318 54640 return nodep;
7319 }
7320
7321 AstNodeExpr* spliceCvtD(AstNodeExpr* nodep) {
7322 // For integer used in REAL context, convert to real
7323 // We don't warn here, "2.0 * 2" is common and reasonable
7324 if (nodep && !nodep->dtypep()->skipRefp()->isDouble()) {
7325 UINFO(6, " spliceCvtD: " << nodep << endl);
7326 VNRelinker linker;
7327 nodep->unlinkFrBack(&linker);
7328 AstNodeExpr* newp;
7329 if (nodep->dtypep()->skipRefp()->isSigned()) {
7330 newp = new AstISToRD{nodep->fileline(), nodep};
7331 } else {
7332 newp = new AstIToRD{nodep->fileline(), nodep};
7333 }
7334 linker.relink(newp);
7335 return newp;
7336 } else {
7337 return nodep;
7338 }
7339 }
7340 AstNodeExpr* spliceCvtS(AstNodeExpr* nodep, bool warnOn, int width) {
7341 // IEEE-2012 11.8.1: Signed: Type coercion creates signed
7342 // 11.8.2: Argument to convert is self-determined
7343 if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
7344 UINFO(6, " spliceCvtS: " << nodep << endl);
7345 VNRelinker linker;
7346 nodep->unlinkFrBack(&linker);
7347 if (const AstConst* const constp = VN_CAST(nodep, Const)) {
7348 // We convert to/from int32_t rather than use floor() as want to make sure is
7349 // representable in integer's number of bits
7350 if (constp->isDouble() && V3Number::epsilonIntegral(constp->num().toDouble())) {
7351 warnOn = false;
7352 }
7353 }
7354 if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer");
7355 AstNodeExpr* const newp = new AstRToIRoundS{nodep->fileline(), nodep};
7356 linker.relink(newp);
7357 newp->dtypeSetBitSized(width, VSigning::SIGNED);
7358 return newp;
7359 } else {
7360 return nodep;
7361 }
7362 }
7363 AstNodeExpr* spliceCvtString(AstNodeExpr* nodep) {
7364 // IEEE-2012 11.8.1: Signed: Type coercion creates signed
7365 // 11.8.2: Argument to convert is self-determined
7366 if (nodep && !(nodep->dtypep()->basicp() && nodep->dtypep()->basicp()->isString())) {
7367 UINFO(6, " spliceCvtString: " << nodep << endl);
7368 VNRelinker linker;
7369 nodep->unlinkFrBack(&linker);
7370 AstNodeExpr* const newp = new AstCvtPackString{nodep->fileline(), nodep};
7371 linker.relink(newp);
7372 return newp;
7373 } else {
7374 return nodep;
7375 }
7376 }
7377 1045 AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) {
7378 // Given a signed/unsigned node type, create the opposite type
7379 // Return new node or nullptr if nothing
7380
2/2
✓ Branch 1 taken 966 times.
✓ Branch 2 taken 79 times.
1045 if (signedFlavorNeeded == nodep->signedFlavor()) return nullptr;
7381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79 times.
79 if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp());
7382 // To simplify callers, some node types don't need to change
7383
1/10
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 79 times.
79 switch (nodep->type()) {
7384 case VNType::atEq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7385 case VNType::atNeq: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7386 case VNType::atEqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7387 case VNType::atNeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7388 case VNType::atEqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7389 case VNType::atNeqWild: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7390 case VNType::atAdd: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7391 case VNType::atSub: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7392 case VNType::atShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return nullptr;
7393 default: break;
7394 }
7395 FileLine* const fl = nodep->fileline();
7396 AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
7397 AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
7398 AstNodeBiop* newp = nullptr;
7399
1/17
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 79 times.
✗ Branch 16 not taken.
79 switch (nodep->type()) {
7400 case VNType::atGt: newp = new AstGtS{fl, lhsp, rhsp}; break;
7401 case VNType::atGtS: newp = new AstGt{fl, lhsp, rhsp}; break;
7402 case VNType::atGte: newp = new AstGteS{fl, lhsp, rhsp}; break;
7403 case VNType::atGteS: newp = new AstGte{fl, lhsp, rhsp}; break;
7404 case VNType::atLt: newp = new AstLtS{fl, lhsp, rhsp}; break;
7405 case VNType::atLtS: newp = new AstLt{fl, lhsp, rhsp}; break;
7406 case VNType::atLte: newp = new AstLteS{fl, lhsp, rhsp}; break;
7407 case VNType::atLteS: newp = new AstLte{fl, lhsp, rhsp}; break;
7408 case VNType::atDiv: newp = new AstDivS{fl, lhsp, rhsp}; break;
7409 case VNType::atDivS: newp = new AstDiv{fl, lhsp, rhsp}; break;
7410 case VNType::atModDiv: newp = new AstModDivS{fl, lhsp, rhsp}; break;
7411 case VNType::atModDivS: newp = new AstModDiv{fl, lhsp, rhsp}; break;
7412 case VNType::atMul: newp = new AstMulS{fl, lhsp, rhsp}; break;
7413 case VNType::atMulS: newp = new AstMul{fl, lhsp, rhsp}; break;
7414 case VNType::atShiftR: newp = new AstShiftRS{fl, lhsp, rhsp}; break;
7415 158 case VNType::atShiftRS: newp = new AstShiftR{fl, lhsp, rhsp}; break;
7416 default: // LCOV_EXCL_LINE
7417 nodep->v3fatalSrc("Node needs sign change, but bad case: " << nodep);
7418 break;
7419 }
7420
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 79 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
79 UINFO(6, " ReplaceWithUOrSVersion: " << nodep << " w/ " << newp << endl);
7421 79 nodep->replaceWith(newp);
7422 newp->dtypeFrom(nodep);
7423 VL_DO_DANGLING(pushDeletep(nodep), nodep);
7424 79 return newp;
7425 }
7426 AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) {
7427 // Given a signed/unsigned node type, create the opposite type
7428 // Return new node or nullptr if nothing
7429 if (nodep->doubleFlavor()) return nullptr;
7430 FileLine* const fl = nodep->fileline();
7431 AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
7432 AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
7433 AstNodeBiop* newp = nullptr;
7434 // No width change on output;... // All below have bool or double outputs
7435 switch (nodep->type()) {
7436 case VNType::atAdd: newp = new AstAddD{fl, lhsp, rhsp}; break;
7437 case VNType::atSub: newp = new AstSubD{fl, lhsp, rhsp}; break;
7438 case VNType::atPow: newp = new AstPowD{fl, lhsp, rhsp}; break;
7439 case VNType::atEq:
7440 case VNType::atEqCase: newp = new AstEqD{fl, lhsp, rhsp}; break;
7441 case VNType::atNeq:
7442 case VNType::atNeqCase: newp = new AstNeqD{fl, lhsp, rhsp}; break;
7443 case VNType::atGt:
7444 case VNType::atGtS: newp = new AstGtD{fl, lhsp, rhsp}; break;
7445 case VNType::atGte:
7446 case VNType::atGteS: newp = new AstGteD{fl, lhsp, rhsp}; break;
7447 case VNType::atLt:
7448 case VNType::atLtS: newp = new AstLtD{fl, lhsp, rhsp}; break;
7449 case VNType::atLte:
7450 case VNType::atLteS: newp = new AstLteD{fl, lhsp, rhsp}; break;
7451 case VNType::atDiv:
7452 case VNType::atDivS: newp = new AstDivD{fl, lhsp, rhsp}; break;
7453 case VNType::atMul:
7454 case VNType::atMulS: newp = new AstMulD{fl, lhsp, rhsp}; break;
7455 default: // LCOV_EXCL_LINE
7456 nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
7457 break;
7458 }
7459 UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp << endl);
7460 nodep->replaceWith(newp);
7461 // No width change; the default created type (bool or double) is correct
7462 VL_DO_DANGLING(pushDeletep(nodep), nodep);
7463 return newp;
7464 }
7465 AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) {
7466 // Given a signed/unsigned node type, replace with string version
7467 // Return new node or nullptr if nothing
7468 if (nodep->stringFlavor()) return nullptr;
7469 FileLine* const fl = nodep->fileline();
7470 AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
7471 AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
7472 AstNodeBiop* newp = nullptr;
7473 // No width change on output;... // All below have bool or double outputs
7474 switch (nodep->type()) {
7475 case VNType::atEq:
7476 case VNType::atEqCase: newp = new AstEqN{fl, lhsp, rhsp}; break;
7477 case VNType::atNeq:
7478 case VNType::atNeqCase: newp = new AstNeqN{fl, lhsp, rhsp}; break;
7479 case VNType::atGt:
7480 case VNType::atGtS: newp = new AstGtN{fl, lhsp, rhsp}; break;
7481 case VNType::atGte:
7482 case VNType::atGteS: newp = new AstGteN{fl, lhsp, rhsp}; break;
7483 case VNType::atLt:
7484 case VNType::atLtS: newp = new AstLtN{fl, lhsp, rhsp}; break;
7485 case VNType::atLte:
7486 case VNType::atLteS: newp = new AstLteN{fl, lhsp, rhsp}; break;
7487 default: // LCOV_EXCL_LINE
7488 nodep->v3fatalSrc("Node needs conversion to string, but bad case: " << nodep);
7489 break;
7490 }
7491 UINFO(6, " ReplaceWithNVersion: " << nodep << " w/ " << newp << endl);
7492 nodep->replaceWith(newp);
7493 // No width change; the default created type (bool or string) is correct
7494 VL_DO_DANGLING(pushDeletep(nodep), nodep);
7495 return newp;
7496 }
7497 AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) {
7498 // Given a signed/unsigned node type, create the opposite type
7499 // Return new node or nullptr if nothing
7500 if (nodep->doubleFlavor()) return nullptr;
7501 FileLine* const fl = nodep->fileline();
7502 AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
7503 AstNodeUniop* newp = nullptr;
7504 switch (nodep->type()) {
7505 case VNType::atNegate: newp = new AstNegateD{fl, lhsp}; break;
7506 default: // LCOV_EXCL_LINE
7507 nodep->v3fatalSrc("Node needs conversion to double, but bad case: " << nodep);
7508 break;
7509 }
7510 UINFO(6, " ReplaceWithDVersion: " << nodep << " w/ " << newp << endl);
7511 nodep->replaceWith(newp);
7512 newp->dtypeFrom(nodep);
7513 VL_DO_DANGLING(pushDeletep(nodep), nodep);
7514 return newp;
7515 }
7516
7517 //----------------------------------------------------------------------
7518 // METHODS - strings
7519
7520 void replaceWithSFormat(AstMethodCall* nodep, const string& format) {
7521 // For string.itoa and similar, replace with SFormatF
7522 const AstArg* argp = VN_CAST(nodep->pinsp(), Arg);
7523 if (!argp) {
7524 nodep->v3error("Argument needed for string." + nodep->prettyName() + " method");
7525 return;
7526 }
7527 AstNodeVarRef* const fromp = VN_AS(nodep->fromp()->unlinkFrBack(), VarRef);
7528 AstNode* const newp = new AstAssign{
7529 nodep->fileline(), fromp,
7530 new AstSFormatF{nodep->fileline(), format, false, argp->exprp()->unlinkFrBack()}};
7531 fromp->access(VAccess::WRITE);
7532 pushDeletep(nodep->backp());
7533 VL_DO_DANGLING(nodep->backp()->replaceWith(newp), newp);
7534 }
7535
7536 //----------------------------------------------------------------------
7537 // METHODS - data types
7538
7539 3694 AstNodeDType* iterateEditMoveDTypep(AstNode* parentp, AstNodeDType* dtnodep) {
7540
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
3694 UASSERT_OBJ(dtnodep, parentp, "Caller should check for nullptr before computing dtype");
7541 // Iterate into a data type to resolve that type.
7542 // The data type may either:
7543 // 1. Be a child (typically getChildDTypep() returns it)
7544 // DTypes at parse time get added as these to some node types
7545 // such as AstVars.
7546 // This function will move it to global scope (that is #2
7547 // will now apply).
7548 // 2. Be under the Netlist and pointed to by an Ast member variable
7549 // (typically refDTypep() or dtypep() returns it)
7550 // so removing/changing a variable won't lose the dtype
7551
7552 // Case #1 above applies?
7553 3694 const bool child1 = (parentp->getChildDTypep() == dtnodep);
7554 3694 const bool child2 = (parentp->getChild2DTypep() == dtnodep);
7555
1/2
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
3694 if (child1 || child2) {
7556
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3694 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
3694 UINFO(9, "iterateEditMoveDTypep child iterating " << dtnodep << endl);
7557 // Iterate, this might edit the dtypes which means dtnodep now lost
7558 VL_DO_DANGLING(userIterate(dtnodep, nullptr), dtnodep);
7559 // Figure out the new dtnodep, remained a child of parent so find it there
7560
1/2
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
3694 dtnodep = child1 ? parentp->getChildDTypep() : parentp->getChild2DTypep();
7561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
3694 UASSERT_OBJ(dtnodep, parentp, "iterateEditMoveDTypep lost pointer to child");
7562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
3694 UASSERT_OBJ(dtnodep->didWidth(), parentp,
7563 "iterateEditMoveDTypep didn't get width resolution of "
7564 << dtnodep->prettyTypeName());
7565 // Move to under netlist
7566
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3694 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
3694 UINFO(9, "iterateEditMoveDTypep child moving " << dtnodep << endl);
7567 dtnodep->unlinkFrBack();
7568 v3Global.rootp()->typeTablep()->addTypesp(dtnodep);
7569 }
7570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3694 times.
3694 if (!dtnodep->didWidth()) {
7571 UINFO(9, "iterateEditMoveDTypep pointer iterating " << dtnodep << endl);
7572 // See notes in visit(AstBracketArrayDType*)
7573 UASSERT_OBJ(!VN_IS(dtnodep, BracketArrayDType), parentp,
7574 "Brackets should have been iterated as children");
7575 userIterate(dtnodep, nullptr);
7576 UASSERT_OBJ(dtnodep->didWidth(), parentp,
7577 "iterateEditMoveDTypep didn't get width resolution");
7578 }
7579 3694 return dtnodep;
7580 }
7581
7582 AstConst* dimensionValue(FileLine* fileline, AstNodeDType* nodep, VAttrType attrType,
7583 int dim) {
7584 // Return the dimension value for the specified attribute and constant dimension
7585 AstNodeDType* dtypep = nodep->skipRefp();
7586 VNumRange declRange; // ranged() set false
7587 for (int i = 1; i <= dim; ++i) {
7588 // UINFO(9, " dim at "<<dim<<" "<<dtypep<<endl);
7589 declRange = VNumRange{}; // ranged() set false
7590 if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
7591 declRange = adtypep->declRange();
7592 if (i < dim) dtypep = adtypep->subDTypep()->skipRefp();
7593 continue;
7594 } else if (const AstNodeUOrStructDType* const adtypep
7595 = VN_CAST(dtypep, NodeUOrStructDType)) {
7596 declRange = adtypep->declRange();
7597 break; // Sub elements don't look like arrays and can't iterate into
7598 } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
7599 if (adtypep->isRanged()) declRange = adtypep->declRange();
7600 break;
7601 }
7602 break; // LCOV_EXCL_LINE
7603 }
7604 AstConst* valp = nullptr; // If nullptr, construct from val
7605 int val = 0;
7606 switch (attrType) {
7607 case VAttrType::DIM_BITS: {
7608 int bits = 1;
7609 while (dtypep) {
7610 // UINFO(9, " bits at "<<bits<<" "<<dtypep<<endl);
7611 if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
7612 bits *= adtypep->declRange().elements();
7613 dtypep = adtypep->subDTypep()->skipRefp();
7614 continue;
7615 } else if (const AstNodeUOrStructDType* const adtypep
7616 = VN_CAST(dtypep, NodeUOrStructDType)) {
7617 bits *= adtypep->width();
7618 break;
7619 } else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
7620 bits *= adtypep->width();
7621 break;
7622 }
7623 break;
7624 }
7625 if (dim == 0) {
7626 val = 0;
7627 } else if (dim == 1 && !declRange.ranged()
7628 && bits == 1) { // $bits should be sane for non-arrays
7629 val = nodep->width();
7630 } else {
7631 val = bits;
7632 }
7633 break; // LCOV_EXCL_LINE
7634 }
7635 case VAttrType::DIM_HIGH: val = !declRange.ranged() ? 0 : declRange.hi(); break;
7636 case VAttrType::DIM_LEFT: val = !declRange.ranged() ? 0 : declRange.left(); break;
7637 case VAttrType::DIM_LOW: val = !declRange.ranged() ? 0 : declRange.lo(); break;
7638 case VAttrType::DIM_RIGHT: val = !declRange.ranged() ? 0 : declRange.right(); break;
7639 case VAttrType::DIM_INCREMENT:
7640 val = (declRange.ranged() && declRange.ascending()) ? -1 : 1;
7641 break;
7642 case VAttrType::DIM_SIZE: val = !declRange.ranged() ? 0 : declRange.elements(); break;
7643 default: nodep->v3fatalSrc("Missing DIM ATTR type case"); break;
7644 }
7645 if (!valp) valp = new AstConst{fileline, AstConst::Signed32{}, val};
7646 UINFO(9, " $dimension " << attrType.ascii() << "(" << cvtToHex(dtypep) << "," << dim
7647 << ")=" << valp << endl);
7648 return valp;
7649 }
7650 AstVar* dimensionVarp(AstNodeDType* nodep, VAttrType attrType, uint32_t msbdim) {
7651 // Return a variable table which has specified dimension properties for this variable
7652 const auto pair = m_tableMap.emplace(std::piecewise_construct, //
7653 std::forward_as_tuple(nodep, attrType),
7654 std::forward_as_tuple(nullptr));
7655 if (pair.second) {
7656 AstNodeArrayDType* const vardtypep
7657 = new AstUnpackArrayDType{nodep->fileline(), nodep->findSigned32DType(),
7658 new AstRange(nodep->fileline(), msbdim, 0)};
7659 AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
7660 v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
7661 AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
7662 "__Vdimtab_" + VString::downcase(attrType.ascii())
7663 + cvtToStr(m_dtTables++),
7664 vardtypep};
7665 varp->isConst(true);
7666 varp->isStatic(true);
7667 varp->valuep(initp);
7668 // Add to root, as don't know module we are in, and aids later structure sharing
7669 v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp);
7670 // Element 0 is a non-index and has speced values
7671 initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, 0));
7672 for (unsigned i = 1; i < msbdim + 1; ++i) {
7673 initp->addValuep(dimensionValue(nodep->fileline(), nodep, attrType, i));
7674 }
7675 userIterate(varp, nullptr); // May have already done $unit so must do this var
7676 pair.first->second = varp;
7677 }
7678 return pair.first->second;
7679 }
7680 static uint64_t enumMaxValue(const AstNode* errNodep, const AstEnumDType* adtypep) {
7681 // Most enums unless overridden are 32 bits, so we size array
7682 // based on max enum value used.
7683 // Ideally we would have a fast algorithm when a number is
7684 // of small width and complete and so can use an array, and
7685 // a map for when the value is many bits and sparse.
7686 uint64_t maxval = 0;
7687 for (const AstEnumItem* itemp = adtypep->itemsp(); itemp;
7688 itemp = VN_AS(itemp->nextp(), EnumItem)) {
7689 const AstConst* const vconstp = VN_CAST(itemp->valuep(), Const);
7690 UASSERT_OBJ(vconstp, errNodep, "Enum item without constified value");
7691 if (!vconstp->num().isAnyXZ() && vconstp->toUQuad() >= maxval)
7692 maxval = vconstp->toUQuad();
7693 }
7694 if (adtypep->itemsp()->width() > 64) {
7695 errNodep->v3warn(E_UNSUPPORTED,
7696 "Unsupported: enum next/prev/name method on enum with > 64 bits");
7697 return 64;
7698 }
7699 return maxval;
7700 }
7701 static AstVar* enumVarp(AstEnumDType* const nodep, VAttrType attrType, bool assoc,
7702 uint32_t msbdim) {
7703 // Return a variable table which has specified dimension properties for this variable
7704 const auto pair = nodep->tableMap().emplace(attrType, nullptr);
7705 if (pair.second) {
7706 UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " assoc=" << assoc
7707 << " max=" << msbdim << " for " << nodep << endl);
7708 AstNodeDType* basep;
7709 if (attrType == VAttrType::ENUM_NAME) {
7710 basep = nodep->findStringDType();
7711 } else if (attrType == VAttrType::ENUM_VALID) {
7712 // TODO in theory we could bit-pack the bits in the table, but
7713 // would require additional operations to extract, so only
7714 // would be worth it for larger tables which perhaps could be
7715 // better handled with equation generation?
7716 basep = nodep->findBitDType();
7717 } else {
7718 basep = nodep->dtypep();
7719 }
7720 AstNodeDType* vardtypep;
7721 if (assoc) {
7722 vardtypep = new AstAssocArrayDType{nodep->fileline(), basep, nodep};
7723 } else {
7724 vardtypep = new AstUnpackArrayDType{nodep->fileline(), basep,
7725 new AstRange(nodep->fileline(), msbdim, 0)};
7726 }
7727 AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
7728 v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
7729 AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
7730 "__Venumtab_" + VString::downcase(attrType.ascii())
7731 + cvtToStr(nodep->uniqueNum()),
7732 vardtypep};
7733 varp->lifetime(VLifetime::STATIC);
7734 varp->isConst(true);
7735 varp->isStatic(true);
7736 varp->valuep(initp);
7737 // Add to root, as don't know module we are in, and aids later structure sharing
7738 v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp);
7739
7740 // Default for all unspecified values
7741 if (attrType == VAttrType::ENUM_NAME) {
7742 initp->defaultp(new AstConst{nodep->fileline(), AstConst::String{}, ""});
7743 } else if (attrType == VAttrType::ENUM_NEXT || attrType == VAttrType::ENUM_PREV) {
7744 initp->defaultp(
7745 new AstConst{nodep->fileline(), V3Number{nodep, nodep->width(), 0}});
7746 } else if (attrType == VAttrType::ENUM_VALID) {
7747 initp->defaultp(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
7748 } else {
7749 nodep->v3fatalSrc("Bad case");
7750 }
7751
7752 // Find valid values and populate
7753 UASSERT_OBJ(nodep->itemsp(), nodep, "enum without items");
7754 std::map<uint64_t, AstNodeExpr*> values;
7755 {
7756 AstEnumItem* const firstp = nodep->itemsp();
7757 const AstEnumItem* prevp = firstp; // Prev must start with last item
7758 while (prevp->nextp()) prevp = VN_AS(prevp->nextp(), EnumItem);
7759 for (AstEnumItem* itemp = firstp; itemp;) {
7760 AstEnumItem* const nextp = VN_AS(itemp->nextp(), EnumItem);
7761 const AstConst* const vconstp = VN_AS(itemp->valuep(), Const);
7762 UASSERT_OBJ(vconstp, nodep, "Enum item without constified value");
7763 if (!vconstp->num().isAnyXZ()) { // Can 2-state runtime decode
7764 const uint64_t i = vconstp->toUQuad();
7765 if (attrType == VAttrType::ENUM_NAME) {
7766 values[i] = new AstConst{nodep->fileline(), AstConst::String{},
7767 itemp->name()};
7768 } else if (attrType == VAttrType::ENUM_NEXT) {
7769 values[i]
7770 = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const
7771 } else if (attrType == VAttrType::ENUM_PREV) {
7772 values[i] = prevp->valuep()->cloneTree(false); // A const
7773 } else if (attrType == VAttrType::ENUM_VALID) {
7774 values[i] = new AstConst{nodep->fileline(), AstConst::BitTrue{}};
7775 } else {
7776 nodep->v3fatalSrc("Bad case");
7777 }
7778 }
7779 prevp = itemp;
7780 itemp = nextp;
7781 }
7782 }
7783 // Add all specified values to table
7784 if (assoc) {
7785 for (const auto& itr : values) initp->addIndexValuep(itr.first, itr.second);
7786 } else {
7787 for (uint64_t i = 0; i < (msbdim + 1); ++i) {
7788 if (values[i]) initp->addIndexValuep(i, values[i]);
7789 }
7790 }
7791 varp->didWidth(true); // May have already done $unit so must do this var
7792 pair.first->second = varp;
7793 }
7794 return pair.first->second;
7795 }
7796
7797 static AstNodeExpr* enumSelect(AstNodeExpr* nodep, AstEnumDType* adtypep, VAttrType attrType) {
7798 // Return expression to get given attrType information from a enum's value (nodep)
7799 // Need a runtime lookup table. Yuk.
7800 const uint64_t msbdim = enumMaxValue(nodep, adtypep);
7801 const bool assoc = msbdim > ENUM_LOOKUP_BITS;
7802 AstNodeExpr* newp;
7803 if (assoc) {
7804 AstVar* const varp = enumVarp(adtypep, attrType, true, 0);
7805 newp = new AstAssocSel{nodep->fileline(), newVarRefDollarUnit(varp), nodep};
7806 } else {
7807 const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit
7808 AstVar* const varp = enumVarp(adtypep, attrType, false, (1ULL << selwidth) - 1);
7809 newp = new AstArraySel{
7810 nodep->fileline(), newVarRefDollarUnit(varp),
7811 // Select in case widths are off due to msblen!=width
7812 // We return "random" values if outside the range, which is fine
7813 // as next/previous on illegal values just need something good out
7814 new AstSel{nodep->fileline(), nodep, 0, selwidth}};
7815 }
7816 if (attrType == VAttrType::ENUM_NAME) {
7817 newp->dtypeSetString();
7818 } else if (attrType == VAttrType::ENUM_VALID) {
7819 newp->dtypeSetBit();
7820 } else {
7821 newp->dtypeFrom(adtypep); // To prevent a later ENUMVALUE
7822 }
7823 return newp;
7824 }
7825 static AstNodeExpr* enumTestValid(AstNodeExpr* valp, AstEnumDType* enumDtp) {
7826 const uint64_t maxval = enumMaxValue(valp, enumDtp);
7827 const bool assoc = maxval > ENUM_LOOKUP_BITS;
7828 AstNodeExpr* testp = nullptr;
7829 FileLine* const fl_novalue = new FileLine{valp->fileline()};
7830 fl_novalue->warnOff(V3ErrorCode::ENUMVALUE, true);
7831 fl_novalue->warnOff(V3ErrorCode::CMPCONST, true);
7832 if (assoc) {
7833 AstVar* const varp = enumVarp(enumDtp, VAttrType::ENUM_VALID, true, 0);
7834 testp = new AstAssocSel{fl_novalue, newVarRefDollarUnit(varp), valp};
7835 } else {
7836 const int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit
7837 AstVar* const varp
7838 = enumVarp(enumDtp, VAttrType::ENUM_VALID, false, (1ULL << selwidth) - 1);
7839 FileLine* const fl_nowidth = new FileLine{fl_novalue};
7840 fl_nowidth->warnOff(V3ErrorCode::WIDTH, true);
7841 testp = new AstCond{
7842 fl_novalue,
7843 new AstGt{fl_nowidth, valp->cloneTreePure(false),
7844 new AstConst{fl_nowidth, AstConst::Unsized64{}, maxval}},
7845 new AstConst{fl_novalue, AstConst::BitFalse{}},
7846 new AstArraySel{fl_novalue, newVarRefDollarUnit(varp),
7847 new AstSel{fl_novalue, valp->cloneTreePure(false), 0, selwidth}}};
7848 }
7849 return testp;
7850 }
7851
7852 PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) {
7853 PatVecMap patmap;
7854 int element = range.left();
7855 for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
7856 patp = VN_AS(patp->nextp(), PatMember)) {
7857 if (patp->keyp()) {
7858 if (patp->varrefp()) V3Const::constifyParamsEdit(patp->varrefp());
7859 if (const AstConst* const constp = VN_CAST(patp->keyp(), Const)) {
7860 element = constp->toSInt();
7861 } else if (const AstConst* const constp = VN_CAST(patp->varrefp(), Const)) {
7862 element = constp->toSInt();
7863 } else {
7864 patp->keyp()->v3error("Assignment pattern key not supported/understood: "
7865 << patp->keyp()->prettyTypeName());
7866 }
7867 }
7868 const bool newEntry = patmap.emplace(element, patp).second;
7869 if (!newEntry) {
7870 patp->v3error("Assignment pattern key used multiple times: " << element);
7871 }
7872 element += range.leftToRightInc();
7873 }
7874 return patmap;
7875 }
7876
7877 void patConcatConvertRecurse(AstPattern* patternp, AstConcat* nodep) {
7878 if (AstConcat* lhsp = VN_CAST(nodep->lhsp(), Concat)) {
7879 patConcatConvertRecurse(patternp, lhsp);
7880 } else {
7881 AstPatMember* const newp = new AstPatMember{
7882 nodep->lhsp()->fileline(), nodep->lhsp()->unlinkFrBack(), nullptr, nullptr};
7883 newp->isConcat(true);
7884 patternp->addItemsp(newp);
7885 }
7886 if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) {
7887 patConcatConvertRecurse(patternp, rhsp);
7888 } else {
7889 AstPatMember* const newp = new AstPatMember{
7890 nodep->rhsp()->fileline(), nodep->rhsp()->unlinkFrBack(), nullptr, nullptr};
7891 newp->isConcat(true);
7892 patternp->addItemsp(newp);
7893 }
7894 }
7895
7896 void makeOpenArrayShell(AstNodeFTaskRef* nodep) {
7897 UINFO(4, "Replicate openarray function " << nodep->taskp() << endl);
7898 AstNodeFTask* const oldTaskp = nodep->taskp();
7899 oldTaskp->dpiOpenParentInc();
7900 UASSERT_OBJ(!oldTaskp->dpiOpenChild(), oldTaskp,
7901 "DPI task should be parent or child, not both");
7902 AstNodeFTask* const newTaskp = oldTaskp->cloneTree(false);
7903 newTaskp->dpiOpenChild(true);
7904 newTaskp->dpiOpenParentClear();
7905 newTaskp->name(newTaskp->name() + "__Vdpioc" + cvtToStr(oldTaskp->dpiOpenParent()));
7906 oldTaskp->addNextHere(newTaskp);
7907 // Relink reference to new function
7908 nodep->taskp(newTaskp);
7909 nodep->name(nodep->taskp()->name());
7910 // Replace open array arguments with the callee's task
7911 const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
7912 for (const auto& tconnect : tconnects) {
7913 AstVar* const portp = tconnect.first;
7914 const AstArg* const argp = tconnect.second;
7915 const AstNode* const pinp = argp->exprp();
7916 if (!pinp) continue; // Argument error we'll find later
7917 if (hasOpenArrayIterateDType(portp->dtypep())) portp->dtypep(pinp->dtypep());
7918 }
7919 }
7920
7921 bool markHasOpenArray(AstNodeFTask* nodep) {
7922 bool hasOpen = false;
7923 for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
7924 if (AstVar* const portp = VN_CAST(stmtp, Var)) {
7925 if (portp->isDpiOpenArray() || hasOpenArrayIterateDType(portp->dtypep())) {
7926 portp->isDpiOpenArray(true);
7927 hasOpen = true;
7928 }
7929 }
7930 }
7931 return hasOpen;
7932 }
7933 bool hasOpenArrayIterateDType(AstNodeDType* nodep) {
7934 // Return true iff this datatype or child has an openarray
7935 if (VN_IS(nodep, UnsizedArrayDType)) return true;
7936 if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp());
7937 return false;
7938 }
7939
7940 //----------------------------------------------------------------------
7941 // METHODS - special type detection
7942
7943 4180 void assertAtStatement(AstNode* nodep) {
7944
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4180 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4180 if (VL_UNCOVERABLE(m_vup && !m_vup->selfDtm())) {
7945 UINFO(1, "-: " << m_vup << endl);
7946 nodep->v3fatalSrc("No dtype expected at statement " << nodep->prettyTypeName());
7947 }
7948 4180 }
7949
1/2
✓ Branch 0 taken 3360 times.
✗ Branch 1 not taken.
3360 void checkConstantOrReplace(AstNode* nodep, const string& message) {
7950 // See also V3WidthSel::checkConstantOrReplace
7951 // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us!
7952 if (!VN_IS(nodep, Const)) {
7953 nodep->v3error(message);
7954 nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::Unsized32{}, 1});
7955 VL_DO_DANGLING(pushDeletep(nodep), nodep);
7956 }
7957 3360 }
7958 static AstVarRef* newVarRefDollarUnit(AstVar* nodep) {
7959 AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::READ};
7960 varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
7961 return varrefp;
7962 }
7963 AstNode* nodeForUnsizedWarning(AstNode* nodep) {
7964 // Return a nodep to use for unsized warnings, reporting on child if can
7965 if (nodep->op1p() && nodep->op1p()->dtypep() && !nodep->op1p()->dtypep()->widthSized()) {
7966 return nodep->op1p();
7967 } else if (nodep->op2p() && nodep->op2p()->dtypep()
7968 && !nodep->op2p()->dtypep()->widthSized()) {
7969 return nodep->op2p();
7970 }
7971 return nodep; // By default return this
7972 }
7973 AstRefDType* checkRefToTypedefRecurse(AstNode* nodep, AstTypedef* typedefp) {
7974 // Recurse all children looking for self reference
7975 // This avoids iterateEditMoveDTypep going into a hard to resolve loop
7976 // Only call once for any given typedef, or will become O(n^2)
7977 if (VL_LIKELY(!nodep)) return nullptr;
7978 if (auto* const refp = VN_CAST(nodep, RefDType)) {
7979 if (refp->typedefp() == typedefp) return refp;
7980 }
7981 if (auto* const refp = checkRefToTypedefRecurse(nodep->op1p(), typedefp)) return refp;
7982 if (auto* const refp = checkRefToTypedefRecurse(nodep->op2p(), typedefp)) return refp;
7983 if (auto* const refp = checkRefToTypedefRecurse(nodep->op3p(), typedefp)) return refp;
7984 if (auto* const refp = checkRefToTypedefRecurse(nodep->op4p(), typedefp)) return refp;
7985 return nullptr;
7986 }
7987
7988 //----------------------------------------------------------------------
7989 // METHODS - special iterators
7990 // These functions save/restore the AstNUser information so it can pass to child nodes.
7991
7992 AstNode* userIterateSubtreeReturnEdits(AstNode* nodep, WidthVP* vup) {
7993
3/18
✓ Branch 0 taken 21893 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 944 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 41929 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
64766 if (!nodep) return nullptr;
7994 AstNode* ret;
7995 {
7996 VL_RESTORER(m_vup);
7997 116879 m_vup = vup;
7998
6/38
✓ Branch 1 taken 21893 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 43193 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 3578 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 5342 times.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 34 taken 944 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 41929 times.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
116879 ret = iterateSubtreeReturnEdits(nodep);
7999 }
8000 22837 return ret;
8001 }
8002 void userIterate(AstNode* nodep, WidthVP* vup) {
8003
5/40
✓ Branch 0 taken 3694 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 3568 times.
✗ Branch 35 not taken.
✓ Branch 36 taken 2524 times.
✗ Branch 37 not taken.
✓ Branch 38 taken 9309 times.
✓ Branch 39 taken 9149 times.
28244 if (!nodep) return;
8004 VL_RESTORER(m_vup);
8005
2/70
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 3694 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✓ Branch 103 taken 9149 times.
✗ Branch 104 not taken.
12843 m_vup = vup;
8006 iterate(nodep);
8007 }
8008 81209 void userIterateAndNext(AstNode* nodep, WidthVP* vup) {
8009
2/2
✓ Branch 0 taken 72372 times.
✓ Branch 1 taken 8837 times.
81209 if (!nodep) return;
8010
2/2
✓ Branch 0 taken 41200 times.
✓ Branch 1 taken 31172 times.
72372 if (nodep->didWidth()) return; // Avoid iterating list we have already iterated
8011
1/2
✓ Branch 1 taken 41200 times.
✗ Branch 2 not taken.
41200 VL_RESTORER(m_vup);
8012 41200 m_vup = vup;
8013
1/2
✓ Branch 1 taken 41200 times.
✗ Branch 2 not taken.
41200 iterateAndNextNull(nodep);
8014 }
8015 void userIterateChildren(AstNode* nodep, WidthVP* vup) {
8016
2/32
✓ Branch 0 taken 480 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 175 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
655 if (!nodep) return;
8017 VL_RESTORER(m_vup);
8018 815 m_vup = vup;
8019
3/80
✓ Branch 1 taken 480 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 160 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 175 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
815 iterateChildren(nodep);
8020 }
8021 void userIterateChildrenBackwardsConst(AstNode* nodep, WidthVP* vup) {
8022
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 if (!nodep) return;
8023 VL_RESTORER(m_vup);
8024 80 m_vup = vup;
8025
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 iterateChildrenBackwardsConst(nodep);
8026 }
8027
8028 public:
8029 // CONSTRUCTORS
8030 21893 WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only.
8031 bool doGenerate) // [in] TRUE if we are inside a generate statement and
8032 // // don't wish to trigger errors
8033 21893 : m_paramsOnly{paramsOnly}
8034 21893 , m_doGenerate{doGenerate} {}
8035 21893 AstNode* mainAcceptEdit(AstNode* nodep) {
8036 43786 return userIterateSubtreeReturnEdits(nodep, WidthVP{SELF, BOTH}.p());
8037 }
8038 43786 ~WidthVisitor() override = default;
8039 };
8040
8041 //######################################################################
8042 // Width class functions
8043
8044 80 void V3Width::width(AstNetlist* nodep) {
8045
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
80 UINFO(2, __FUNCTION__ << ": " << endl);
8046 {
8047 // We should do it in bottom-up module order, but it works in any order.
8048 const WidthClearVisitor cvisitor{nodep};
8049 80 WidthVisitor visitor{false, false};
8050
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 (void)visitor.mainAcceptEdit(nodep);
8051 WidthRemoveVisitor rvisitor;
8052 (void)rvisitor.mainAcceptEdit(nodep);
8053 80 } // Destruct before checking
8054
1/2
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
80 V3Global::dumpCheckGlobalTree("width", 0, dumpTreeEitherLevel() >= 3);
8055 80 }
8056 //! Single node parameter propagation
8057 //! Smaller step... Only do a single node for parameter propagation
8058 21813 AstNode* V3Width::widthParamsEdit(AstNode* nodep) {
8059
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 21813 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
21813 UINFO(4, __FUNCTION__ << ": " << nodep << endl);
8060 // We should do it in bottom-up module order, but it works in any order.
8061 21813 WidthVisitor visitor{true, false};
8062
1/2
✓ Branch 1 taken 21813 times.
✗ Branch 2 not taken.
21813 nodep = visitor.mainAcceptEdit(nodep);
8063 // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
8064 21813 return nodep;
8065 21813 }
8066
8067 //! Single node parameter propagation for generate blocks.
8068 //! Smaller step... Only do a single node for parameter propagation
8069 //! If we are inside a generated "if", "case" or "for", we don't want to
8070 //! trigger warnings when we deal with the width. It is possible that
8071 //! these are spurious, existing within sub-expressions that will not
8072 //! actually be generated. Since such occurrences, must be constant, in
8073 //! order to be something a generate block can depend on, we can wait until
8074 //! later to do the width check.
8075 //! @return Pointer to the edited node.
8076 AstNode* V3Width::widthGenerateParamsEdit(
8077 AstNode* nodep) { //!< [in] AST whose parameters widths are to be analyzed.
8078 UINFO(4, __FUNCTION__ << ": " << nodep << endl);
8079 // We should do it in bottom-up module order, but it works in any order.
8080 WidthVisitor visitor{true, true};
8081 nodep = visitor.mainAcceptEdit(nodep);
8082 // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
8083 return nodep;
8084 }
8085